Скрипты для торрент трекеров

Страницы :   Пред.  1, 2, 3 ... 5, 6, 7 ... 12, 13, 14  След.
Ответить
 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 17-Ноя-17 11:00 (7 лет назад)

Papant
Не получается, Ctrl-O так и делает, а Greasemonkey не видит открытого файла, его содержимое просто показывается в окне. Разработчик пишет, что теперь расширения не имеют доступа к файловой системе, видимо в этом причина.
[Профиль]  [ЛС] 

Papant

Admin

Стаж: 17 лет 2 месяца

Сообщений: 56369

Papant · 17-Ноя-17 11:03 (спустя 2 мин.)

unchqua
А слеши вводил правые , и в нужном количестве?
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 17-Ноя-17 11:22 (спустя 19 мин.)

Papant
Ну вот так у меня сейчас:

Тренируюсь на старом скрипте.
[Профиль]  [ЛС] 

Rugodiv

Стаж: 9 лет 7 месяцев

Сообщений: 605

Rugodiv · 17-Ноя-17 11:47 (спустя 25 мин., ред. 18-Ноя-17 14:44)

Violentmonkey на Firefox 57.0, работает на 5+ https://addons.mozilla.org/en-US/firefox/addon/violentmonkey/
[Профиль]  [ЛС] 

SergeyJD

VIP (Заслуженный)

Стаж: 16 лет 9 месяцев

Сообщений: 4859

SergeyJD · 17-Ноя-17 14:10 (спустя 2 часа 22 мин.)

Откатился до 56.
Буду ждать новостей.
[Профиль]  [ЛС] 

порошков

Moderator

Стаж: 15 лет 9 месяцев

Сообщений: 24373

порошков · 17-Ноя-17 16:45 (спустя 2 часа 35 мин.)

SergeyJD писал(а):
74249909Откатился до 56.
Тоже откатился, но обезьяна уже не хочет ставиться Хотя недавно все работало норм...
скрытый текст
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 17-Ноя-17 17:09 (спустя 23 мин.)

порошков
Взял xpi из своего Фаерфокса. В нём написано, что это версия 4.0, но в Firefox 56 работает. Не помню только, не запрещена ли в браузере установка приложений не из AMO.
https://yadi.sk/d/nGYayQNx3PnmsH
[Профиль]  [ЛС] 

порошков

Moderator

Стаж: 15 лет 9 месяцев

Сообщений: 24373

порошков · 17-Ноя-17 17:16 (спустя 7 мин., ред. 17-Ноя-17 17:32)

unchqua
Ничего не ставится(((
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 17-Ноя-17 17:22 (спустя 5 мин., ред. 09-Фев-18 11:49)

В общем, у меня получается запускать свои скрипты по-новому, изменения минимальны. Проблема сейчас в том, как их устанавливать на другие компы в свете этого:
Цитата:
Если раньше можно было установить скрипт с локального диска, открыв его про Ctrl-O, то теперь нужен веб-сервер, отдающий исходники скриптов, да ещё и с нужным Content-Type.
У кого есть подконтрольный веб-сервер, тот может ставить уже сейчас (исходники ниже), кладите под сервер файлы с расширением .user.js в кодировке UTF-8 и обращайтесь к ним по URL-у httр://...
Остальных прошу подождать до понедельника, когда появится нужный хостинг для скриптов.
Ссылка на страницу всех скриптов в моей подписи.
Внимание! По последней информации, по какой-то причине накрылся API WebStorage, с помощью которого скрипты хранили свои настройки. Скрипты 16 и 21 пока работают неполноценно: хранят свои настройки только до закрытия закладки браузера. Ищу способ восстановления работы. Те скрипты, где настройки не нужны, переделать несложно.
[Профиль]  [ЛС] 

Hannibal61

Консультант Техпомощи

Стаж: 14 лет 8 месяцев

Сообщений: 18111

Hannibal61 · 17-Ноя-17 21:45 (спустя 4 часа)

порошков писал(а):
74250793Тоже откатился, но обезьяна уже не хочет ставиться
порошков
Там есть ссылка на старые версии, чуть ниже.

[Профиль]  [ЛС] 

порошков

Moderator

Стаж: 15 лет 9 месяцев

Сообщений: 24373

порошков · 17-Ноя-17 22:06 (спустя 20 мин.)

Hannibal61
Уфф...спасибо, сделал
[Профиль]  [ЛС] 

Nataly-Lilly

Moderator gray

Стаж: 16 лет 5 месяцев

Сообщений: 9603

Nataly-Lilly · 19-Ноя-17 21:28 (спустя 1 день 23 часа)

Откатилась на 56. Модскрипт не заработал. что делать?
[Профиль]  [ЛС] 

Hannibal61

Консультант Техпомощи

Стаж: 14 лет 8 месяцев

Сообщений: 18111

Hannibal61 · 19-Ноя-17 21:35 (спустя 6 мин.)

Nataly-Lilly
Попробуйте Greasemonkey переустановить.
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 20-Ноя-17 10:29 (спустя 12 часов)

У нас появился хостинг скриптов!
Надеюсь, он долго будет в строю, а мы с вами его наполним тем, что было написано для старых версий Firefox.
Вот что сейчас там лежит (их описание тут):
http://kotproxy.info/gmscripts/rto13.user.js
http://kotproxy.info/gmscripts/rto16.user.js
http://kotproxy.info/gmscripts/rto18.user.js
http://kotproxy.info/gmscripts/rto21.user.js
Это всё скрипты для FF 57+, а версии предыдущего формата есть в теме.
Рутрекер 09. Дополнительные кнопки для текстового редактора сообщений. Это код-заготовка для показа того, как можно добавить свои кнопки, вставляющие какой-то текст в редактор, то есть каждый сам может дописать там свои и использовать их.
скрытый текст
Код:
// ==UserScript==
// @name           Рутрекер 09. Дополнительные кнопки для текстового редактора сообщений.
// @namespace      http://www.unchqua.ru/ns/greasemonkey
// @include        *://rutr.life/forum/*
// @include        *://rutracker.net/forum/*
// @include        *://rutracker.cr/forum/*
// @include        *://rutracker.nl/forum/*
// @require        https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
// @version        1
// @grant          none
// ==/UserScript==
(function(){
var $ = jQuery.noConflict(true);
// Панель системных кнопок редактора.
var editor_div = $("div#ped-editor-buttons");
if (editor_div.length !== 1) // Текстового редактора нет - тут делать нечего.
return;
if ($("div#rto09_buttons").length!==0)
  return; // Кнопки уже были добавлены - второй раз не надо.
// Строка с новыми кнопками.
var new_buttons = $("<div id=\"rto09_buttons\" class=\"buttons-row\">");
// Наша кнопка 1:
var new_button_1 =
  $("<input style=\"margin: 0 .3em;\" value=\"\u00a0Разметка 1\u00a0\" name=\"custom_button_1\" title=\"Сложная разметка 1\" type=\"button\"/>")
    .click(function(){
     unsafeWindow.bbcode.surround("[size=10][color=gray][i]","[/i][/color][/size]");
    });
// Наша кнопка 2:
var new_button_2 =
  $("<input style=\"margin: 0 .3em;\" value=\"\u00a0Разметка 2\u00a0\" name=\"custom_button_2\" title=\"Сложная разметка 2\" type=\"button\"/>")
    .click(function(){
     unsafeWindow.bbcode.surround("[size=6][color=orange][u]","[/u][/color][/size]");
    });
// Наша кнопка 3:
var new_button_3 =
  $("<input style=\"margin: 0 .3em;\" value=\"\u00a0Разметка 3\u00a0\" name=\"custom_button_3\" title=\"Сложная разметка 3\" type=\"button\"/>")
    .click(function(){
     unsafeWindow.bbcode.insertAtCursor("aaa\n[b]bbb[/b]\n[i]ccc[/i]");
    });
// Помещаем в строку все наши кнопки, а её добавляем к панели системных кнопок.
editor_div.append(new_buttons.append(new_button_1, new_button_2, new_button_3));
})();
Этот скрипт будет там ближе к ночи, и страницу общего описания тоже сделаю.
Какие скрипты ещё переносить?
[Профиль]  [ЛС] 

RoxMarty

RG Мультфильмы

Стаж: 17 лет 7 месяцев

Сообщений: 14622

RoxMarty · 20-Ноя-17 11:26 (спустя 57 мин., ред. 20-Ноя-17 11:26)

При открытии соответствующих страничек в 57-м кодировка названия/описания сбитая
Цитата:
Какие скрипты ещё переносить?
Спасибо за портирование. Изучим. Потестим. Сообщим
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 20-Ноя-17 12:09 (спустя 42 мин., ред. 20-Ноя-17 18:39)

RoxMarty писал(а):
74270159При открытии соответствующих страничек в 57-м кодировка названия/описания сбитая
У меня то же самое, забей, будет работать, и название в списке потом тоже будет правильное. Это эвристика в браузере или расширении плохо работает, неверно определяет кодировку.
Автор Greasemonkey в своём форуме не пропадает, отвечает на вопросы, это хорошо. Глядишь, будем и дальше жить.
Сделал кое-какое описание скриптов и удобные ссылки на них, и с картинками!
Завтра продолжу, а то уже опух.
[Профиль]  [ЛС] 

RoxMarty

RG Мультфильмы

Стаж: 17 лет 7 месяцев

Сообщений: 14622

RoxMarty · 20-Ноя-17 22:15 (спустя 10 часов)

unchqua
Отличное описание с картинками. Давно этого не хватало!
Ждём-с переделки автооткрытия новых комментов
[Профиль]  [ЛС] 

maximus_lt

Moderator gray

Стаж: 17 лет 5 месяцев

Сообщений: 6144

maximus_lt · 20-Ноя-17 23:30 (спустя 1 час 15 мин.)

unchqua, такими темпами мы доберемся до авто заполнения релизов с Discogs и Кинопоиска
[Профиль]  [ЛС] 

Rugodiv

Стаж: 9 лет 7 месяцев

Сообщений: 605

Rugodiv · 21-Ноя-17 11:56 (спустя 12 часов)

Обновилось дополнение NoScript , теперь оно работает на Firefox 57+ https://addons.mozilla.org/ru/firefox/addon/noscript/
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 21-Ноя-17 18:53 (спустя 6 часов)

Забросил ещё пару скриптов и обновил список, ознакомьтесь. Какие скрипты ещё нужны?
Начинают вырисовываться неприятные моменты в связи с переходом на новый формат расширений, но пока как-то можно жить.
[Профиль]  [ЛС] 

maximus_lt

Moderator gray

Стаж: 17 лет 5 месяцев

Сообщений: 6144

maximus_lt · 21-Ноя-17 19:44 (спустя 51 мин., ред. 21-Ноя-17 19:44)

unchqua, вы можете глянуть YADG (1.4.38) под Violentmonkey 2.8.0 (под Maxthon 5.1.3.2000) перестал работать на Реде.
скрытый текст
Код:
// ==UserScript==
// @id             pth-yadg
// @name           RED YADG
// @description    This script provides integration with online description generator YADG (http://yadg.cc) - Credit to Slack06
// @license        https://github.com/SavageCore/yadg-pth-userscript/blob/master/LICENSE
// @version        1.4.38
// @namespace      yadg
// @grant          GM_xmlhttpRequest
// @grant          GM.xmlHttpRequest
// @require        https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require        https://yadg.cc/static/js/jsandbox.min.js
// @include        http*://*redacted.ch/upload.php*
// @include        http*://*redacted.ch/requests.php*
// @include        http*://*redacted.ch/torrents.php*
// @include        http*://*waffles.ch/upload.php*
// @include        http*://*waffles.ch/requests.php*
// @downloadURL    https://github.com/SavageCore/yadg-pth-userscript/raw/master/pth_yadg.user.js
// ==/UserScript==
// --------- USER SETTINGS START ---------
/* global window unsafeWindow document GM JSandbox formatName AddArtistField RemoveArtistField Blob alert $ Image */
/* eslint max-depth: ['off'], block-scoped-var: 'off', no-loop-func: 'off', no-alert: 'off' */
/*
Here you can set site specific default templates.
You can find a list of available templates at: https://yadg.cc/api/v2/templates/
*/
var defaultPTHFormat = 4;
var defaultWafflesFormat = 9;
var defaultPTHTarget = 'other';
var defaultPTHDescriptionTarget = 'album';
var yadg;
var factory;
var yadgRenderer;
var yadgTemplates;
// --------- USER SETTINGS END ---------
function fetchImage(target, callback) {
var imgElement = document.getElementById('image');
if (imgElement && imgElement.getAttribute('disabled') === 'disabled') {
  return;
}
var link;
if (target === null) {
  link = unsafeWindow.$('#yadg_input').val();
} else {
  link = target;
}
switch (true) {
  case (/discogs/.test(link)):
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: link,
    onload: function (response) {
     if (response.status === 200) {
      var container = document.implementation.createHTMLDocument().documentElement;
      container.innerHTML = response.responseText;
      if (typeof callback === 'function') {
       callback(JSON.parse(container.querySelectorAll('div.image_gallery.image_gallery_large')[0].getAttribute('data-images'))[0].full);
      }
     }
    }
   });
   break;
  case (/itunes/.test(link)):
      var regex = /apple\.com\/(?:([a-z]{2,3})\/)?.*\/(?:(\d+)|id(\d*))/;
      var res = regex.exec(link);
   var id = res[2] | res[3];
   var country = 'us';
   if (res[1]) {
    country = res[1];
   }
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: 'https://itunes.apple.com/lookup?id=' + id + '&country=' + country,
    onload: function (response) {
     if (response.status === 200) {
      var data = JSON.parse(response.responseText);
      var hires = data.results[0].artworkUrl100.replace('100x100bb', '100000x100000-999');
      if (typeof callback === 'function') {
       callback(hires);
      }
     }
    }
   });
   break;
  case (/bandcamp/.test(link)):
  case (factory.getScraperSelect().value === 'bandcamp'):
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: link,
    onload: function (response) {
     if (response.status === 200) {
      var container = document.implementation.createHTMLDocument().documentElement;
      container.innerHTML = response.responseText;
      var scaledImg = container.querySelectorAll('#tralbumArt > a > img')[0].src;
      var originalImg = scaledImg.replace(/_16/, '_0');
      var tempImg = new Image();
      tempImg.src = originalImg;
      tempImg.onload = function () {
       if (this.width === this.height) {
        var img = originalImg;
       } else {
        img = scaledImg;
       }
       if (typeof callback === 'function') {
        callback(img);
       }
      };
     }
    }
   });
   break;
  case (/beatport/.test(link)):
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: link,
    onload: function (response) {
     if (response.status === 200) {
      var container = document.implementation.createHTMLDocument().documentElement;
      container.innerHTML = response.responseText;
      if (typeof callback === 'function') {
       callback(container.querySelectorAll('div.interior-release-chart-artwork-parent > img')[0].src);
      }
     }
    }
   });
   break;
  case (/musicbrainz/.test(link)):
   var regex = /release\/(.*)/;
   var id = regex.exec(link)[1];
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    headers: {
     'User-Agent': 'YADG/1.3.17 (yadg.cc)'
    },
    method: 'GET',
    url: 'http://coverartarchive.org/release/' + id + '/',
    onload: function (response) {
     if (response.status === 200) {
      var data = JSON.parse(response.responseText);
      if (typeof callback === 'function') {
       callback(data.images[0].image);
      }
     }
    }
   });
   break;
  case (/junodownload/.test(link)):
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: link,
    onload: function (response) {
     if (response.status === 200) {
      var container = document.implementation.createHTMLDocument().documentElement;
      container.innerHTML = response.responseText;
      if (typeof callback === 'function') {
       callback(container.querySelectorAll('#product_image_front > a')[0].href);
      }
     }
    }
   });
   break;
  case (/metal-archives/.test(link)):
   GM.xmlHttpRequest ({ // eslint-disable-line new-cap
    method: 'GET',
    url: link,
    onload: function (response) {
     if (response.status === 200) {
      var container = document.implementation.createHTMLDocument().documentElement;
      container.innerHTML = response.responseText;
      var parser = document.createElement('a');
      parser.href = container.querySelectorAll('#cover > img')[0].src;
      var imgLink = parser.protocol + '//' + parser.hostname + parser.pathname;
      if (typeof callback === 'function') {
       callback(imgLink);
      }
     }
    }
   });
   break;
  default:
   break;
}
}
function pthImgIt() {
var pthImgIt = document.getElementsByClassName('rehost_it_cover')[0];
switch (window.location.href) {
  case (window.location.href.match(/\/upload\.php/) || {}).input:
   var imgElement = document.getElementById('image').value;
   break;
  case (window.location.href.match(/torrents\.php\?action=editgroup/) || {}).input:
   var imgElement = document.querySelectorAll('#content > div > div:nth-child(2) > form > div > input[type="text"]:nth-child(5)')[0].value;
   break;
  default:
   break;
}
if (pthImgIt && imgElement) {
  pthImgIt.click();
}
}
function insertImage(img, callback) {
switch (window.location.href) {
  case (window.location.href.match(/\/upload\.php/) || {}).input:
   var input = document.getElementById('image');
   input.value = img;
   if (input.getAttribute('autorehost') === 'true') {
    var evt = document.createEvent('HTMLEvents');
    evt.initEvent('keyup', false, true);
    input.dispatchEvent(evt);
   }
   input.parentNode.parentNode.insertAdjacentHTML('beforebegin', '<tr id="yadg_image_preview_tr"><td class="label">Album Art Preview:</td><td><img id="yadg_image_preview" src="' + img + '" width="300px" /></tr></td>');
   callback();
   break;
  case (window.location.href.match(/torrents\.php\?action=editgroup/) || {}).input:
   var imageInputElement = document.querySelectorAll('#content > div > div:nth-child(2) > form > div > input[type="text"]:nth-child(5)')[0];
   imageInputElement.value = img;
   imageInputElement.parentNode.insertAdjacentHTML('beforebegin', '<div id="yadg_image_preview_div"><img id="yadg_image_preview" src="' + img + '" width="300px" /></div>');
   callback();
   break;
    case (window.location.href.match(/requests\.php\?/) || {}).input:
   var imageInputElement = document.querySelectorAll('#image_tr > td:nth-child(2) > input[type="text"]:nth-child(1)')[0];
   imageInputElement.value = img;
   imageInputElement.parentNode.parentNode.insertAdjacentHTML('beforebegin', '<tr id="yadg_image_preview_tr"><td class="label">Album Art Preview:</td><td><img id="yadg_image_preview" src="' + img + '" width="300px" /></tr></td>');
   callback();
   break;
  default:
   break;
}
}
// --------- THIRD PARTY CODE AREA START ---------
//
// Creates an object which gives some helper methods to
// Save/Load/Remove data to/from the localStorage
//
// Source from: https://github.com/gergob/localstoragewrapper
//
function LocalStorageWrapper(applicationPrefix) {
'use strict';
if (applicationPrefix === undefined) {
  throw new Error('applicationPrefix parameter should be defined');
}
var delimiter = '_';
// if the passed in value for prefix is not string, it should be converted
var keyPrefix = typeof (applicationPrefix) === 'string' ? applicationPrefix : JSON.stringify(applicationPrefix);
var localStorage = window.localStorage || unsafeWindow.localStorage;
var isLocalStorageAvailable = function () {
  return typeof (localStorage) !== 'undefined';
};
var getKeyPrefix = function () {
  return keyPrefix;
};
//
// validates if there is a prefix defined for the keys
// and checks if the localStorage functionality is available or not
//
var makeChecks = function (key) {
  var prefix = getKeyPrefix();
  if (prefix === undefined) {
   throw new Error('No prefix was defined, data cannot be saved');
  }
  if (!isLocalStorageAvailable()) {
   throw new Error('LocalStorage is not supported by your browser, data cannot be saved');
  }
  // keys are always strings
  var checkedKey = typeof (key) === 'string' ? key : JSON.stringify(key);
  return checkedKey;
};
//
// saves the value associated to the key into the localStorage
//
var addItem = function (key, value) {
  var that = this;
  try {
   var checkedKey = makeChecks(key);
   var combinedKey = that.getKeyPrefix() + delimiter + checkedKey;
   localStorage.setItem(combinedKey, JSON.stringify(value));
  }  catch (err) {
   console.log(err);
   throw err;
  }
};
//
// gets the value of the object saved to the key passed as parameter
//
var getItem = function (key) {
  var that = this;
  var result;
  try {
   var checkedKey = makeChecks(key);
   var combinedKey = that.getKeyPrefix() + delimiter + checkedKey;
   var resultAsJSON = localStorage.getItem(combinedKey);
   result = JSON.parse(resultAsJSON);
  }  catch (err) {
   console.log(err);
   throw err;
  }
  return result;
};
//
// returns all the keys from the localStorage
//
var getAllKeys = function () {
  var prefix = getKeyPrefix();
  var results = [];
  if (prefix === undefined) {
   throw new Error('No prefix was defined, data cannot be saved');
  }
  if (!isLocalStorageAvailable()) {
   throw new Error('LocalStorage is not supported by your browser, data cannot be saved');
  }
  for (var key in localStorage) {
   if (key.indexOf(prefix) === 0) {
    var keyParts = key.split(delimiter);
    results.push(keyParts[1]);
   }
  }
  return results;
};
//
// removes the value associated to the key from the localStorage
//
var removeItem = function (key) {
  var that = this;
  var result = false;
  try {
   var checkedKey = makeChecks(key);
   var combinedKey = that.getKeyPrefix() + delimiter + checkedKey;
   localStorage.removeItem(combinedKey);
   result = true;
  }  catch (err) {
   console.log(err);
   throw err;
  }
  return result;
};
//
// removes all the values from the localStorage
//
var removeAll = function () {
  var that = this;
  try {
   var allKeys = that.getAllKeys();
   for (var i = 0; i < allKeys.length; ++i) {
    var checkedKey = makeChecks(allKeys[i]);
    var combinedKey = that.getKeyPrefix() + delimiter + checkedKey;
    localStorage.removeItem(combinedKey);
   }
  }  catch (err) {
   console.log(err);
   throw err;
  }
};
// make some of the functionalities public
return {
  isLocalStorageAvailable: isLocalStorageAvailable,
  getKeyPrefix: getKeyPrefix,
  addItem: addItem,
  getItem: getItem,
  getAllKeys: getAllKeys,
  removeItem: removeItem,
  removeAll: removeAll
};
}
// --------- THIRD PARTY CODE AREA END ---------
var yadgUtil = {
exec: function (fn) {
  var script = document.createElement('script');
  script.setAttribute('type', 'application/javascript');
  script.textContent = '(' + fn + ')();';
  document.body.appendChild(script); // run the script
  document.body.removeChild(script); // clean up
},
// handle for updating page css, taken from one of hateradio's scripts
addCSS: function (style) {
  if (!this.style) {
   this.style = document.createElement('style');
   this.style.type = 'text/css';
   (document.head || document.getElementsByTagName('head')[0]).appendChild(this.style);
  }
  this.style.appendChild(document.createTextNode(style + '\n'));
},
setValueIfSet: function (value, input, cond) {
  if (cond) {
   input.value = value;
  } else {
   input.value = '';
  }
},
  // negative count will remove, positive count will add given number of artist boxes
addRemoveArtistBoxes: function (count) {
  if (count !== 0) {
   if (count < 0) {
    for (var i = 0; i < -count; i++) {
     yadgUtil.exec(function () {
      RemoveArtistField(); // eslint-disable-line new-cap
     });
    }
   } else {
    for (i = 0; i < count; i++) {
     yadgUtil.exec(function () {
      AddArtistField(); // eslint-disable-line new-cap
     });
    }
   }
  }
},
getOptionOffsets: function (select) {
  var optionOffsets = {};
  for (var j = 0; j < select.options.length; j++) {
   optionOffsets[select.options[j].value] = select.options[j].index;
  }
  return optionOffsets;
},
storage: new LocalStorageWrapper('yadg'),
settings: new LocalStorageWrapper('yadgSettings')
};
// very simple wrapper for XmlHttpRequest
function requester(url, method, callback, data, errorCallback) { // eslint-disable-line max-params
this.data = data;
this.url = url;
this.method = method;
if (!errorCallback) {
  errorCallback = yadg.failedCallback;
}
this.send = function () {
  var details = {
   url: this.url,
   method: this.method,
   onload: function (response) {
    if (response.status === 200) {
     callback(JSON.parse(response.responseText));
    } else if (response.status === 401) {
     yadg.failedAuthenticationCallback();
    } else {
     errorCallback();
    }
   },
   onerror: errorCallback
  };
  if (method === 'POST') {
   details.data = JSON.stringify(this.data);
  }
  var headers = {
   Accept: 'application/json',
   'Content-Type': 'application/json'
  };
  if (yadgUtil.settings.getItem(factory.KEY_API_TOKEN)) {
   headers.Authorization = 'Token ' + yadgUtil.settings.getItem(factory.KEY_API_TOKEN);
  }
  details.headers = headers;
  GM.xmlHttpRequest (details); // eslint-disable-line new-cap
};
}
var yadgSandbox = {
KEY_LAST_WARNING: 'templateLastWarning',
init: function (callback) {
  GM.xmlHttpRequest ({ // eslint-disable-line new-cap
   method: 'GET',
   url: yadg.yadgHost + '/static/js/jsandbox-worker.js',
   onload: function (response) {
    var script;
    var dataURL = null;
    if (response.status === 200) {
     script = response.responseText;
     var blob = new Blob([script], {type: 'application/javascript'});
     var URL = window.URL || window.webkitURL;
     if (!URL || !URL.createObjectURL) {
      throw new Error('No no valid implementation of window.URL.createObjectURL found.');
     }
     dataURL = URL.createObjectURL(blob);
     yadgSandbox.initCallback(dataURL);
     yadgSandbox.loadSwig(callback);
    } else {
     yadgSandbox.initCallbackError();
    }
   },
   onerror: function () {
    yadgSandbox.initCallbackError();
   }
  });
},
loadSwig: function (callback) {
  // importScripts for the web worker will not work in Firefox with cross-domain requests
  // see: https://bugzilla.mozilla.org/show_bug.cgi?id=756589
  // so download the Swig files manually with GM.xmlHttpRequest
  GM.xmlHttpRequest ({ // eslint-disable-line new-cap
   method: 'GET',
   url: yadg.yadgHost + '/static/js/swig.min.js',
   onload: function (response) {
    if (response.status === 200) {
     yadgSandbox.swigScript = response.responseText;
     GM.xmlHttpRequest ({ // eslint-disable-line new-cap
      method: 'GET',
      url: yadg.yadgHost + '/static/js/swig.custom.js',
      onload: function (response) {
       if (response.status === 200) {
        yadgSandbox.swigCustomScript = response.responseText;
        callback();
       }
      }
     });
    }
   }
  });
},
initializeSwig: function (dependencies) {
  if (!(this.swigScript && this.swigCustomScript)) {
   yadg.failedCallback();
   return;
  }
  yadgSandbox.exec({data: this.swigScript, onerror: yadg.failedCallback});
  yadgSandbox.exec({data: this.swigCustomScript, onerror: yadg.failedCallback});
  yadgSandbox.exec({data: 'var myswig = new swig.Swig({ loader: swig.loaders.memory(input.templates), autoescape: false }), i=0; yadg_filters.register_filters(myswig);', input: {templates: dependencies}});
},
renderTemplate: function (template, data, callback, error) {
  var evalString = 'myswig.render(input.template, { locals: input.data, filename: \'scratchpad\' + (i++) })';
  this.eval({data: evalString, callback: function (out) {
   callback(out);
  }, input: {template: template, data: data}, onerror: function (err) {
   error(err);
  }});
},
initCallback: function (dataUrl) {
  JSandbox.url = dataUrl;
  this.jsandbox = new JSandbox();
  this.initError = false;
},
resetSandbox: function () {
  this.jsandbox.terminate();
  this.jsandbox = new JSandbox();
},
load: function (options) {
  this.jsandbox.load(options);
},
exec: function (options) {
  this.jsandbox.exec(options);
},
eval: function (options) {
  this.jsandbox.eval(options);
},
initCallbackError: function () {
  this.initError = true;
  var lastWarning = yadgUtil.storage.getItem(this.KEY_LAST_WARNING);
  var now = new Date();
  if (lastWarning === null || now.getTime() - (new Date(lastWarning)).getTime() > factory.CACHE_TIMEOUT) {
   console.log('Could not load the necessary script files for executing YADG. If this error persists you might need to update the user script. You will only get this message once a day.');
   yadgUtil.storage.addItem(this.KEY_LAST_WARNING, now);
  }
}
};
factory = {
// storage keys for cache
KEY_LAST_CHECKED: 'lastChecked',
KEY_SCRAPER_LIST: 'scraperList',
KEY_FORMAT_LIST: 'formatList',
// storage keys for settings
KEY_API_TOKEN: 'apiToken',
KEY_DEFAULT_TEMPLATE: 'defaultTemplate',
KEY_DEFAULT_TARGET: 'defaultTarget',
KEY_DESCRIPTION_TARGET: 'descriptionTarget',
KEY_DEFAULT_SCRAPER: 'defaultScraper',
KEY_REPLACE_DESCRIPTION: 'replaceDescriptionOn',
KEY_SETTINGS_INIT_VER: 'settingsInitializedVer',
KEY_FETCH_IMAGE: 'fetchImage',
KEY_AUTO_PREVIEW: 'autoPreview',
KEY_AUTO_REHOST: 'autoRehost',
CACHE_TIMEOUT: 1000 * 60 * 60 * 24, // 24 hours
UPDATE_PROGRESS: 0,
locations: [
  {
   name: 'pth_upload',
   regex: /http(s)?:\/\/(.*\.)?redacted\.ch\/upload\.php.*/i
  },
  {
   name: 'pth_edit',
   regex: /http(s)?:\/\/(.*\.)?redacted\.ch\/torrents\.php\?action=editgroup&groupid=.*/i
  },
  {
   name: 'pth_request',
   regex: /http(s)?:\/\/(.*\.)?redacted\.ch\/requests\.php\?action=new/i
  },
  {
   name: 'pth_request_edit',
   regex: /http(s)?:\/\/(.*\.)?redacted\.ch\/requests\.php\?action=edit&id=.*/i
  },
  {
   name: 'pth_torrent_overview',
   regex: /http(s)?:\/\/(.*\.)?redacted\.ch\/torrents\.php\?id=.*/i
  },
  {
   name: 'waffles_upload',
   regex: /http(s)?:\/\/(.*\.)?waffles\.ch\/upload\.php.*/i
  },
  {
   name: 'waffles_request',
   regex: /http(s)?:\/\/(.*\.)?waffles\.ch\/requests\.php\?do=add/i
  }
],
determineLocation: function (uri) {
  for (var i = 0; i < this.locations.length; i++) {
   if (this.locations[i].regex.test(uri)) {
    return this.locations[i].name;
   }
  }
  return null;
},
init: function () {
  this.currentLocation = this.determineLocation(document.URL);
  // only continue with the initialization if we found a valid location
  if (this.currentLocation === null) {
   return false;
  }
  this.insertIntoPage(this.getInputElements());
  // set the necessary styles
  this.setStyles();
  // make sure we initialize the settings to the most recent version
  this.initializeSettings();
  // populate settings inputs
  this.populateSettings();
  // add the appropriate action for the button
  var button = document.getElementById('yadg_submit');
  button.addEventListener('click', function (e) {
   e.preventDefault();
   yadg.makeRequest();
   if (factory.getFetchImageCheckbox().checked) {
    fetchImage(null, function (data) {
     insertImage(data, function () {
      if (factory.getAutoRehostCheckbox() && factory.getAutoRehostCheckbox().checked) {
       pthImgIt();
      }
     });
    });
   }
  }, false);
  // add the action for the options toggle
  var toggleLink = document.getElementById('yadg_toggle_options');
  if (toggleLink !== null) {
   toggleLink.addEventListener('click', function (e) {
    e.preventDefault();
    var optionsDiv = document.getElementById('yadg_options');
    var display = optionsDiv.style.display;
    if (display === 'none' || display === '') {
     optionsDiv.style.display = 'block';
    } else {
     optionsDiv.style.display = 'none';
    }
   });
  }
  // add the action for the template select
  var formatSelect = this.getFormatSelect();
  if (formatSelect !== null) {
   formatSelect.addEventListener('change', function () {
    if (yadgRenderer.hasCached()) {
     yadgRenderer.renderCached(this.value, factory.setDescriptionBoxValue, factory.setDescriptionBoxValue);
    }
   });
  }
  // // add the action for the target select
  // var targetSelect = this.getTargetSelect();
  // if (targetSelect !== null) {
  //  targetSelect.addEventListener('change', function (e) {
  //   var target = this.value;
  //  });
  // }
  // add the action to the save settings link
  var saveSettingsLink = document.getElementById('yadg_save_settings');
  if (saveSettingsLink !== null) {
   saveSettingsLink.addEventListener('click', function (e) {
    e.preventDefault();
    factory.saveSettings();
    alert('Settings saved successfully.');
   });
  }
  // add the action to the clear cache link
  var clearCacheLink = document.getElementById('yadg_clear_cache');
  if (clearCacheLink !== null) {
   clearCacheLink.addEventListener('click', function (e) {
    e.preventDefault();
    yadgUtil.storage.removeAll();
    alert('Cache cleared. Please reload the page for this to take effect.');
   });
  }
  var lastChecked = yadgUtil.storage.getItem(factory.KEY_LAST_CHECKED);
  if (lastChecked === null || (new Date()).getTime() - (new Date(lastChecked)).getTime() > factory.CACHE_TIMEOUT) {
   // update the scraper and formats list
   factory.UPDATE_PROGRESS = 1;
   yadg.getScraperList(factory.setScraperSelect);
   yadg.getFormatsList(factory.setFormatSelect);
  } else {
   factory.setScraperSelect(yadgUtil.storage.getItem(factory.KEY_SCRAPER_LIST));
   factory.setFormatSelect(yadgUtil.storage.getItem(factory.KEY_FORMAT_LIST));
  }
  return true;
},
getApiTokenInput: function () {
  return document.getElementById('yadg_api_token');
},
getReplaceDescriptionCheckbox: function () {
  return document.getElementById('yadg_options_replace');
},
getFetchImageCheckbox: function () {
  return document.getElementById('yadg_options_image');
},
getAutoRehostCheckbox: function () {
  return document.getElementById('yadg_options_rehost');
},
getAutoPreviewCheckbox: function () {
  return document.getElementById('yadg_options_preview');
},
getReplaceDescriptionSettingKey: function () {
  return this.makeReplaceDescriptionSettingsKey(this.currentLocation);
},
makeReplaceDescriptionSettingsKey: function (subKey) {
  return this.KEY_REPLACE_DESCRIPTION + subKey.replace(/_/g, '');
},
initializeSettings: function () {
  var settingsVer = yadgUtil.settings.getItem(factory.KEY_SETTINGS_INIT_VER);
  var currentVer = 1;
  if (!settingsVer) {
   settingsVer = 0;
  }
  if (settingsVer < currentVer) {
   // replace descriptions on upload and new request pages
   var locations = [
    'pth_upload',
    'pth_request',
    'waffles_upload',
    'waffles_upload_new',
    'waffles_request'
   ];
   for (var i = 0; i < locations.length; i++) {
    var loc = locations[i];
    var replaceDescSettingKey = factory.makeReplaceDescriptionSettingsKey(loc);
    yadgUtil.settings.addItem(replaceDescSettingKey, true);
   }
  }
  yadgUtil.settings.addItem(factory.KEY_SETTINGS_INIT_VER, currentVer);
},
populateSettings: function () {
  var apiToken = yadgUtil.settings.getItem(factory.KEY_API_TOKEN);
  var replaceDesc = yadgUtil.settings.getItem(factory.getReplaceDescriptionSettingKey());
  var fetchImage = yadgUtil.settings.getItem(factory.KEY_FETCH_IMAGE);
  var autoRehost = yadgUtil.settings.getItem(factory.KEY_AUTO_REHOST);
  var autoPreview = yadgUtil.settings.getItem(factory.KEY_AUTO_PREVIEW);
  var descriptionTarget = yadgUtil.settings.getItem(factory.KEY_AUTO_PREVIEW);
  if (apiToken) {
   var apiTokenInput = factory.getApiTokenInput();
   apiTokenInput.value = apiToken;
  }
  if (replaceDesc) {
   var replaceDescCheckbox = factory.getReplaceDescriptionCheckbox();
   replaceDescCheckbox.checked = true;
  }
  if (fetchImage) {
   var fetchImageCheckbox = factory.getFetchImageCheckbox();
   fetchImageCheckbox.checked = true;
  }
  if (autoRehost) {
   var autoRehostCheckbox = factory.getAutoRehostCheckbox();
   if (autoRehostCheckbox) {
    autoRehostCheckbox.checked = true;
   }
  }
  if (autoPreview && window.location.href.match(/\/upload\.php/)) {
   var autoPreviewCheckbox = factory.getAutoPreviewCheckbox();
   autoPreviewCheckbox.checked = true;
  }
},
saveSettings: function () {
  var scraperSelect = factory.getScraperSelect();
  var templateSelect = factory.getFormatSelect();
  var targetSelect = factory.getTargetSelect();
  var descriptionTargetSelect = factory.getDescriptionTargetSelect();
  var apiTokenInput = factory.getApiTokenInput();
  var replaceDescCheckbox = factory.getReplaceDescriptionCheckbox();
  var fetchImageCheckbox = factory.getFetchImageCheckbox();
  var autoRehostCheckbox = factory.getAutoRehostCheckbox();
  var autoPreviewCheckbox = factory.getAutoPreviewCheckbox();
  var currentScraper = null;
  var currentTemplate = null;
  var currentTarget = null;
  var currentDescriptionTarget = null;
  var apiToken = apiTokenInput.value.trim();
  var replaceDescription = replaceDescCheckbox.checked;
  var fetchImage = fetchImageCheckbox.checked;
  if (autoRehostCheckbox) {
   var autoRehost = autoRehostCheckbox.checked;
  }
  if (window.location.href.match(/\/upload\.php/)) {
   var autoPreview = autoPreviewCheckbox.checked;
  }
  if (scraperSelect.options.length > 0) {
   currentScraper = scraperSelect.options[scraperSelect.selectedIndex].value;
  }
  if (templateSelect.options.length > 0) {
   currentTemplate = templateSelect.options[templateSelect.selectedIndex].value;
  }
  if (targetSelect.options.length > 0) {
   currentTarget = targetSelect.options[targetSelect.selectedIndex].value;
  }
  if (descriptionTargetSelect.options.length > 0) {
   currentDescriptionTarget = descriptionTargetSelect.options[descriptionTargetSelect.selectedIndex].value;
  }
  if (currentScraper !== null) {
   yadgUtil.settings.addItem(factory.KEY_DEFAULT_SCRAPER, currentScraper);
  }
  if (currentTemplate !== null) {
   yadgUtil.settings.addItem(factory.KEY_DEFAULT_TEMPLATE, currentTemplate);
  }
  if (currentTarget !== null) {
   yadgUtil.settings.addItem(factory.KEY_DEFAULT_TARGET, currentTarget);
  }
  if (currentDescriptionTarget !== null) {
   yadgUtil.settings.addItem(factory.KEY_DESCRIPTION_TARGET, currentDescriptionTarget);
  }
  if (apiToken === '') {
   yadgUtil.settings.removeItem(factory.KEY_API_TOKEN);
  } else {
   yadgUtil.settings.addItem(factory.KEY_API_TOKEN, apiToken);
  }
  var replaceDescSettingKey = factory.getReplaceDescriptionSettingKey();
  if (replaceDescription) {
   yadgUtil.settings.addItem(replaceDescSettingKey, true);
  } else {
   yadgUtil.settings.removeItem(replaceDescSettingKey);
  }
  if (fetchImage) {
   yadgUtil.settings.addItem(factory.KEY_FETCH_IMAGE, true);
  } else {
   yadgUtil.settings.removeItem(factory.KEY_FETCH_IMAGE);
  }
  if (autoRehost) {
   yadgUtil.settings.addItem(factory.KEY_AUTO_REHOST, true);
  } else if (!autoRehost && autoRehostCheckbox) {
   yadgUtil.settings.removeItem(factory.KEY_AUTO_REHOST);
  }
  if (autoPreview) {
   yadgUtil.settings.addItem(factory.KEY_AUTO_PREVIEW, true);
  } else if (!autoPreview && window.location.href.match(/\/upload\.php/)) {
   yadgUtil.settings.removeItem(factory.KEY_AUTO_PREVIEW);
  }
},
setDescriptionBoxValue: function (value) {
  var descBox = factory.getDescriptionBox();
  var replaceDescCheckbox = factory.getReplaceDescriptionCheckbox();
  var replaceDesc = false;
  if (replaceDescCheckbox !== null) {
   replaceDesc = replaceDescCheckbox.checked;
  }
  if (descBox !== null && !Array.isArray(descBox)) {
   if (!replaceDesc && /\S/.test(descBox.value)) { // check if the current description contains more than whitespace
    descBox.value += '\n\n' + value;
   } else {
    descBox.value = value;
   }
   if (descBox.parentNode.nextSibling.nextSibling) {
    var previewBtn = descBox.parentNode.nextSibling.nextSibling.firstChild.nextSibling;
    if (previewBtn && previewBtn.value === 'Preview' && factory.getAutoPreviewCheckbox().checked) {
     previewBtn.click();
    }
   }
  } else if (Array.isArray(descBox)) {
   for (var i = 0; i < descBox.length; i++) {
    descBox[i].value = value;
    var previewBtn = descBox[i].parentNode.nextSibling.nextSibling.firstChild.nextSibling;
    if (previewBtn && previewBtn.value === 'Preview' && factory.getAutoPreviewCheckbox().checked) {
     previewBtn.click();
    }
   }
  }
},
getFormatSelect: function () {
  return document.getElementById('yadg_format');
},
setDefaultFormat: function () {
  var formatSelect = factory.getFormatSelect();
  var formatOffsets = yadgUtil.getOptionOffsets(formatSelect);
  var defaultFormat = yadgUtil.settings.getItem(factory.KEY_DEFAULT_TEMPLATE);
  if (defaultFormat !== null && defaultFormat in formatOffsets) {
   formatSelect.selectedIndex = formatOffsets[defaultFormat];
  } else {
   // we have no settings so fall back to the hard coded defaults
   switch (this.currentLocation) {
    case 'waffles_upload':
    case 'waffles_upload_new':
    case 'waffles_request':
     formatSelect.selectedIndex = formatOffsets[defaultWafflesFormat];
     break;
    default:
     formatSelect.selectedIndex = formatOffsets[defaultPTHFormat];
     break;
   }
  }
},
getTargetSelect: function () {
  return document.getElementById('yadg_target');
},
getDescriptionTargetSelect: function () {
  return document.getElementById('yadg_description_target');
},
setDefaultTarget: function () {
  var targetSelect = factory.getTargetSelect();
  var targetOffsets = yadgUtil.getOptionOffsets(targetSelect);
  var defaultTarget = yadgUtil.settings.getItem(factory.KEY_DEFAULT_TARGET);
  if (defaultTarget !== null && defaultTarget in targetOffsets) {
   targetSelect.selectedIndex = targetOffsets[defaultTarget];
  } else {
   targetSelect.selectedIndex = targetOffsets[defaultPTHTarget];
  }
},
setDefaultDescriptionTarget: function () {
  var targetDescriptionSelect = factory.getDescriptionTargetSelect();
  var targetDescriptionOffsets = yadgUtil.getOptionOffsets(targetDescriptionSelect);
  var defaultDescriptionTarget = yadgUtil.settings.getItem(factory.KEY_DESCRIPTION_TARGET);
  if (defaultDescriptionTarget !== null && defaultDescriptionTarget in targetDescriptionOffsets) {
   targetDescriptionSelect.selectedIndex = targetDescriptionOffsets[defaultDescriptionTarget];
  } else {
   targetDescriptionSelect.selectedIndex = targetDescriptionOffsets[defaultPTHDescriptionTarget];
  }
},
getScraperSelect: function () {
  return document.getElementById('yadg_scraper');
},
setDefaultScraper: function () {
  var defaultScraper = yadgUtil.settings.getItem(factory.KEY_DEFAULT_SCRAPER);
  if (defaultScraper !== null) {
   var scraperSelect = factory.getScraperSelect();
   var scraperOffsets = yadgUtil.getOptionOffsets(scraperSelect);
   if (defaultScraper in scraperOffsets) {
    scraperSelect.selectedIndex = scraperOffsets[defaultScraper];
   }
  }
},
setScraperSelect: function (scrapers) {
  var scraperSelect = factory.getScraperSelect();
  factory.setSelect(scraperSelect, scrapers);
  factory.setDefaultScraper();
  if (factory.UPDATE_PROGRESS > 0) {
   yadgUtil.storage.addItem(factory.KEY_SCRAPER_LIST, scrapers);
   factory.UPDATE_PROGRESS |= 1 << 1;
   if (factory.UPDATE_PROGRESS === 7) {
    yadgUtil.storage.addItem(factory.KEY_LAST_CHECKED, new Date());
   }
  }
},
setFormatSelect: function (templates) {
  var formatSelect = factory.getFormatSelect();
  var nonUtility = [];
  var saveTemplates = [];
  for (var i = 0; i < templates.length; i++) {
   if (factory.UPDATE_PROGRESS > 0) {
    if (templates[i].name === 'What') {
     templates[i].name = 'RED';
     templates[i].nameFormatted = 'RED';
    } else if (templates[i].name === 'What (Tracks only)') {
     templates[i].name = 'RED (Tracks only)';
     templates[i].nameFormatted = 'RED (Tracks only)';
    }
    yadgTemplates.addTemplate(templates[i]);
    saveTemplates.push({
     id: templates[i].id,
     url: templates[i].url,
     name: templates[i].name,
     nameFormatted: templates[i].nameFormatted,
     owner: templates[i].owner,
     default: templates[i].default,
     isUtility: templates[i].isUtility
    });
   } else {
    if (templates[i].name === 'What') {
     templates[i].name = 'PTH';
     templates[i].nameFormatted = 'PTH';
    } else if (templates[i].name === 'What (Tracks only)') {
     templates[i].name = 'PTH (Tracks only)';
     templates[i].nameFormatted = 'PTH (Tracks only)';
    }
    yadgTemplates.addTemplateUrl(templates[i].id, templates[i].url);
   }
   if (!templates[i].isUtility) {
    nonUtility.push(templates[i]);
   }
  }
  factory.setSelect(formatSelect, nonUtility);
  factory.setDefaultFormat();
  factory.setDefaultTarget();
  factory.setDefaultDescriptionTarget();
  if (factory.UPDATE_PROGRESS > 0) {
   yadgUtil.storage.addItem(factory.KEY_FORMAT_LIST, saveTemplates);
   factory.UPDATE_PROGRESS |= 1 << 2;
   if (factory.UPDATE_PROGRESS === 7) {
    yadgUtil.storage.addItem(factory.KEY_LAST_CHECKED, new Date());
   }
  }
},
setSelect: function (select, data) {
  select.options.length = data.length;
  for (var i = 0; i < data.length; i++) {
   // we are not using the javascript constructor to create an Option instance because this will create an
   // incompatibility with jQuery in Chrome which will make it impossible to add a new artist field on redacted.ch
   var o = document.createElement('option');
   if ('nameFormatted' in data[i]) {
    o.text = data[i].nameFormatted;
   } else {
    o.text = data[i].name;
   }
   o.value = data[i].value || data[i].id;
   o.selected = data[i].default;
   select.options[i] = o;
   if (data[i].default) {
    select.selectedIndex = i;
   }
   if (data[i].url) {
    o.setAttribute('data-url', data[i].url);
   }
  }
},
setStyles: function () {
  // general styles
  yadgUtil.addCSS('div#yadg_options{ display:none; margin-top:3px; } input#yadg_input,input#yadg_submit,label#yadg_format_label,a#yadg_scraper_info { margin-right: 5px } div#yadg_response { margin-top:3px; } select#yadg_scraper { margin-right: 2px } #yadg_options_template,#yadg_options_api_token,#yadg_options_replace_div { margin-bottom: 3px; } .add_form[name="yadg"] input,.add_form[name="yadg"] select { width: 90%; margin: 2px 0 !important; }');
  // location specific styles will go here
  switch (this.currentLocation) {
   case 'waffles_upload':
    yadgUtil.addCSS('div#yadg_response ul { margin-left: 0 !important; padding-left: 0 !important; }');
    break;
   case 'waffles_request':
    yadgUtil.addCSS('div#yadg_response ul { margin-left: 0 !important; padding-left: 0 !important; }');
    break;
   default:
    break;
  }
},
getInputElements: function () {
  var buttonHTML = '<input type="submit" value="Fetch" id="yadg_submit"/>';
  var scraperSelectHTML = '<select name="yadg_scraper" id="yadg_scraper"></select>';
  var optionsHTML = '<div id="yadg_options"><div id="yadg_options_template"><label for="yadg_format" id="yadg_format_label">Template:</label><select name="yadg_format" id="yadg_format"></select></div><div id="yadg_options_target"><label for="yadg_target" id="yadg_target_label">Edition:</label><select name="yadg_target" id="yadg_target"><option value="original">Original</option><option value="other">Other</option></select></div><div id="yadg_options_description_target"><label for="yadg_description_target" id="yadg_description_target_label">Description:</label><select name="yadg_description_target" id="yadg_description_target"><option value="album">Album</option><option value="release">Release</option><option value="both">Both</option></select></div><div id="yadg_options_api_token"><label for="yadg_api_token" id="yadg_api_token_label">API token (<a href="https://yadg.cc/api/token" target="_blank">Get one here</a>):</label> <input type="text" name="yadg_api_token" id="yadg_api_token" size="50" /></div><div id="yadg_options_replace_div"><input type="checkbox" name="yadg_options_replace" id="yadg_options_replace" /> <label for="yadg_options_replace" id="yadg_options_replace_label">Replace descriptions on this page</label></div><div id="yadg_options_image_div"><input type="checkbox" name="yadg_options_image" id="yadg_options_image" /> <label for="yadg_options_image" id="yadg_options_image_label">Auto fetch Album Art (Bandcamp, Beatport, Discogs, iTunes, Junodownload, Metal-Archives, MusicBrainz)</label></div>';
  if (document.getElementsByClassName('rehost_it_cover')[0]) {
   optionsHTML += '<div id="yadg_options_rehost_div"><input type="checkbox" name="yadg_options_rehost" id="yadg_options_rehost" /> <label for="yadg_options_rehost" id="yadg_options_rehost_label">Auto rehost with <a href="https://redacted.ch/forums.php?action=viewthread&threadid=1992">[User Script] PTPIMG URL uploader</a></label></div>';
  }
  if (window.location.href.match(/\/upload\.php/)) {
   optionsHTML += '<div id="yadg_options_preview_div"><input type="checkbox" name="yadg_options_preview" id="yadg_options_preview" /> <label for="yadg_options_preview" id="yadg_options_preview_label">Auto preview description</label></div>';
  }
  optionsHTML += '<div id="yadg_options_links"><a id="yadg_save_settings" href="#" title="Save the currently selected scraper and template as default for this site and save the given API token.">Save settings</a> <span class="yadg_separator">|</span> <a id="yadg_clear_cache" href="#">Clear cache</a></div></div>';
  var inputHTML = '<input type="text" name="yadg_input" id="yadg_input" size="60" />';
  var responseDivHTML = '<div id="yadg_response"></div>';
  var toggleOptionsLinkHTML = '<a id="yadg_toggle_options" href="#">Toggle options</a>';
  var scraperInfoLink = '<a id="yadg_scraper_info" href="https://yadg.cc/available-scrapers" target="_blank" title="Get additional information on the available scrapers">[?]</a>';
  switch (this.currentLocation) {
   case 'pth_upload':
    var tr = document.createElement('tr');
    tr.className = 'yadg_tr';
    tr.innerHTML = '<td class="label">YADG:</td><td>' + inputHTML + scraperSelectHTML + scraperInfoLink + buttonHTML + toggleOptionsLinkHTML + optionsHTML + responseDivHTML + '</td>';
    return tr;
   case 'pth_edit':
    var div = document.createElement('div');
    div.className = 'yadg_div';
    div.innerHTML = '<h3 class="label">YADG:</h3>\n' + inputHTML + '\n' + scraperSelectHTML + '\n' + scraperInfoLink + '\n' + buttonHTML + '\n' + toggleOptionsLinkHTML + '\n' + optionsHTML + '\n' + responseDivHTML;
    return div;
   case 'pth_torrent_overview':
    div = document.createElement('div');
    div.id = 'yadg_div';
    div.className = 'box';
    div.innerHTML = '<div class="head"><strong>YADG</strong></div>\n<div class="body">\n<form class="add_form" name="yadg" method="post">\n<input type="text" name="yadg_input" id="yadg_input" />\n' + scraperSelectHTML + '\n' + scraperInfoLink + '\n' + buttonHTML + '\n' + toggleOptionsLinkHTML + '\n' + optionsHTML + '\n' + responseDivHTML;
    return div;
   case 'pth_request':
   case 'pth_request_edit':
    tr = document.createElement('tr');
    tr.className = 'yadg_tr';
    tr.innerHTML = '<td class="label">YADG:</td><td>' + inputHTML + scraperSelectHTML + scraperInfoLink + buttonHTML + toggleOptionsLinkHTML + optionsHTML + responseDivHTML + '</td>';
    return tr;
   case 'waffles_upload':
    tr = document.createElement('tr');
    tr.className = 'yadg_tr';
    tr.innerHTML = '<td class="heading" valign="top" align="right"><label for="yadg_input">YADG:</label></td><td>' + inputHTML + scraperSelectHTML + scraperInfoLink + buttonHTML + toggleOptionsLinkHTML + optionsHTML + responseDivHTML + '</td>';
    return tr;
   case 'waffles_upload_new':
    var p = document.createElement('p');
    p.className = 'yadg_p';
    p.innerHTML = '<label for="yadg_input">YADG:</label>' + inputHTML + scraperSelectHTML + scraperInfoLink + buttonHTML + toggleOptionsLinkHTML + optionsHTML + responseDivHTML;
    return p;
   case 'waffles_request':
    tr = document.createElement('tr');
    tr.className = 'yadg_tr';
    tr.innerHTML = '<td style="text-align:left;width:100px;">YADG:</td><td style="text-align:left;">' + inputHTML + scraperSelectHTML + scraperInfoLink + buttonHTML + toggleOptionsLinkHTML + optionsHTML + responseDivHTML + '</td>';
    return tr;
   default:
    // this should actually never happen
    return document.createElement('div');
  }
},
insertIntoPage: function (element) {
  switch (this.currentLocation) {
   case 'pth_upload':
    var yearTr = document.getElementById('year_tr');
    yearTr.parentNode.insertBefore(element, yearTr);
    break;
   case 'pth_edit':
    var summaryInput = document.getElementsByName('summary')[0];
    summaryInput.parentNode.insertBefore(element, summaryInput.nextSibling.nextSibling);
    break;
   case 'pth_torrent_overview':
    var addArtistsBox = document.getElementsByClassName('box_addartists')[0];
    addArtistsBox.parentNode.insertBefore(element, addArtistsBox.nextSibling.nextSibling);
    break;
   case 'pth_request':
   case 'pth_request_edit':
    var artistTr = document.getElementById('artist_tr');
    artistTr.parentNode.insertBefore(element, artistTr);
    break;
   case 'waffles_upload':
    var submitButton = document.getElementsByName('submit')[0];
    submitButton.parentNode.parentNode.parentNode.insertBefore(element, submitButton.parentNode.parentNode);
    break;
   case 'waffles_upload_new':
    var h4s = document.getElementsByTagName('h4');
    var div;
    for (var i = 0; i < h4s.length; i++) {
     if (h4s[i].innerHTML.indexOf('read the rules') !== -1) {
      div = h4s[i].parentNode;
      break;
     }
    }
    div.appendChild(element);
    break;
   case 'waffles_request':
    var categorySelect = document.getElementsByName('category')[0];
    categorySelect.parentNode.parentNode.parentNode.insertBefore(element, categorySelect.parentNode.parentNode);
    break;
   default:
    break;
  }
},
getDescriptionBox: function () {
  switch (this.currentLocation) {
   case 'pth_upload':
    if (factory.getDescriptionTargetSelect().value === 'album') {
     return document.getElementById('album_desc');
    } else if (factory.getDescriptionTargetSelect().value === 'release') {
     return document.getElementById('release_desc');
    } else if (factory.getDescriptionTargetSelect().value === 'both') {
     return [document.getElementById('album_desc'), document.getElementById('release_desc')];
    }
    break;
   case 'pth_edit':
    return document.getElementsByName('body')[0];
   case 'pth_torrent_overview':
    if (!{}.hasOwnProperty.call(this, 'dummybox')) {
     this.dummybox = document.createElement('div');
    }
    return this.dummybox;
   case 'pth_request':
   case 'pth_request_edit':
    return document.getElementsByName('description')[0];
   case 'waffles_upload':
    return document.getElementById('descr');
   case 'waffles_upload_new':
    return document.getElementById('id_descr');
   case 'waffles_request':
    return document.getElementsByName('information')[0];
   default:
    // that should actually never happen
    return document.createElement('div');
  }
},
getFormFillFunction: function () {
  var currentTarget = factory.getTargetSelect().value;
  switch (this.currentLocation) {
   case 'pth_upload':
    var f = function (rawData) {
     if (currentTarget === 'other') {
      var remaster = document.getElementById('remaster');
      var albumTitleInput = document.getElementById('title');
      var yearInput = document.getElementById('remaster_year');
      var labelInput = document.getElementById('remaster_record_label');
      var catalogInput = document.getElementById('remaster_catalogue_number');
      remaster.checked = 'checked';
      unsafeWindow.Remaster(); // eslint-disable-line new-cap
      unsafeWindow.CheckYear(); // eslint-disable-line new-cap
     } else {
      albumTitleInput = document.getElementById('title');
      yearInput = document.getElementById('year');
      labelInput = document.getElementById('record_label');
      catalogInput = document.getElementById('catalogue_number');
     }
     if (/itunes/.test(rawData.url)) {
      var releaseTypeInput = document.getElementById('releasetype');
      switch (true) {
       case /.+ - Single$/.test(rawData.title):
        rawData.title = rawData.title.replace(/ - Single$/, '');
        if (releaseTypeInput.getAttribute('disabled') !== 'disabled') {
         releaseTypeInput.value = 9;
        }
        break;
       case /.+ - EP$/.test(rawData.title):
        rawData.title = rawData.title.replace(/ - EP$/, '');
        if (releaseTypeInput.getAttribute('disabled') !== 'disabled') {
         releaseTypeInput.value = 5;
        }
        break;
       default:
        break;
      }
     }
     var artistInputs = document.getElementsByName('artists[]');
     var tagsInput = document.getElementById('tags');
     var data = yadg.prepareRawResponse(rawData);
     var nullArtistCount = 0;
     if (artistInputs[0].getAttribute('disabled') !== 'disabled') {
      if (data.artists === false) {
       for (var i = 0; i < artistInputs.length; i++) {
        artistInputs[i].value = '';
       }
      } else {
       var inputIdx = 0;
       yadgUtil.addRemoveArtistBoxes(data.effective_artist_count - artistInputs.length);
       artistInputs = document.getElementsByName('artists[]');
       for (i = 0; i < data.artist_keys.length; i++) {
        var artistKey = data.artist_keys[i];
        if (artistKey === 'null') {
         nullArtistCount++;
         continue;
        }
        var artistTypes = data.artists[artistKey];
        for (var j = 0; j < artistTypes.length; j++) {
         var artistType = artistTypes[j];
         var artistInput = artistInputs[inputIdx];
         var typeSelect = artistInput.nextSibling;
         while (typeSelect.tagName !== 'SELECT') {
          typeSelect = typeSelect.nextSibling;
         }
         artistInput.value = artistKey;
         var optionOffsets = yadgUtil.getOptionOffsets(typeSelect);
         if (artistType === 'main') {
          typeSelect.selectedIndex = optionOffsets[1];
         } else if (artistType === 'guest') {
          typeSelect.selectedIndex = optionOffsets[2];
         } else if (artistType === 'remixer') {
          typeSelect.selectedIndex = optionOffsets[3];
         } else {
         // we don't know this artist type, default to "main"
          typeSelect.selectedIndex = optionOffsets[1];
         }
        // next artist input
         inputIdx += 1;
        }
       }
       if (nullArtistCount > 0) {
        yadgUtil.addRemoveArtistBoxes(nullArtistCount *= -1);
       }
      }
     }
     if (tagsInput.getAttribute('disabled') !== 'disabled') {
      if (data.tags === false) {
       tagsInput.value = '';
      } else {
       var tagsArray = data.tag_string.split(', ');
       var tagsUnique = tagsArray.filter(function (elem, index, self) {
        return index === self.indexOf(elem);
       });
       tagsInput.value = tagsUnique.join(',').toLowerCase();
      }
     }
     if (yearInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     }
     if (albumTitleInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.title, albumTitleInput, data.title !== false);
     }
     if (labelInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.label, labelInput, data.label !== false);
     }
     if (catalogInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.catalog, catalogInput, data.catalog !== false);
     }
    };
    return f;
   case 'pth_edit':
    f = function (rawData) {
     var summaryInput = document.getElementsByName('summary')[0];
     var yearInput = document.getElementsByName('year')[0];
     var labelInput = document.getElementsByName('record_label')[0];
     var catalogInput = document.getElementsByName('catalogue_number')[0];
     var data = yadg.prepareRawResponse(rawData);
     summaryInput.value = 'YADG Update';
     if (yearInput && yearInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     }
     if (labelInput && labelInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.label, labelInput, data.label !== false);
     }
     if (catalogInput && catalogInput.getAttribute('disabled') !== 'disabled') {
      yadgUtil.setValueIfSet(data.catalog, catalogInput, data.catalog !== false);
     }
    };
    return f;
   case 'pth_torrent_overview':
    f = function (rawData) {
     var artistInputs = document.getElementsByName('aliasname[]');
     var data = yadg.prepareRawResponse(rawData);
     if (data.artists === false) {
      for (var i = 0; i < artistInputs.length; i++) {
       artistInputs[i].value = '';
      }
     } else {
      var inputIdx = 0;
      yadgUtil.addRemoveArtistBoxes(data.effective_artist_count - artistInputs.length);
      artistInputs = document.getElementsByName('aliasname[]');
      for (i = 0; i < data.artist_keys.length; i++) {
       var artistKey = data.artist_keys[i];
       var artistTypes = data.artists[artistKey];
       for (var j = 0; j < artistTypes.length; j++) {
        var artistType = artistTypes[j];
        var artistInput = artistInputs[inputIdx];
        var typeSelect = artistInput.nextSibling;
        while (typeSelect.tagName !== 'SELECT') {
         typeSelect = typeSelect.nextSibling;
        }
        artistInput.value = artistKey;
        var optionOffsets = yadgUtil.getOptionOffsets(typeSelect);
        if (artistType === 'main') {
         typeSelect.selectedIndex = optionOffsets[1];
        } else if (artistType === 'guest') {
         typeSelect.selectedIndex = optionOffsets[2];
        } else if (artistType === 'remixer') {
         typeSelect.selectedIndex = optionOffsets[3];
        } else {
         // we don't know this artist type, default to "main"
         typeSelect.selectedIndex = optionOffsets[1];
        }
        // next artist input
        inputIdx += 1;
       }
      }
     }
    };
    return f;
   case 'pth_request':
   case 'pth_request_edit':
    f = function (rawData) {
     var artistInputs = document.getElementsByName('artists[]');
     var albumTitleInput = document.getElementsByName('title')[0];
     var yearInput = document.getElementsByName('year')[0];
     var labelInput = document.getElementsByName('recordlabel')[0];
     var catalogInput = document.getElementsByName('cataloguenumber')[0];
     var tagsInput = document.getElementById('tags');
     var data = yadg.prepareRawResponse(rawData);
     var nullArtistCount = 0;
     if (data.artists === false) {
      for (var i = 0; i < artistInputs.length; i++) {
       artistInputs[i].value = '';
      }
     } else {
      var inputIdx = 0;
      yadgUtil.addRemoveArtistBoxes(data.effective_artist_count - artistInputs.length);
      artistInputs = document.getElementsByName('artists[]');
      for (i = 0; i < data.artist_keys.length; i++) {
       var artistKey = data.artist_keys[i];
       var artistTypes = data.artists[artistKey];
       if (artistKey === 'null') {
        nullArtistCount++;
        continue;
       }
       for (var j = 0; j < artistTypes.length; j++) {
        var artistType = artistTypes[j];
        var artistInput = artistInputs[inputIdx];
        var typeSelect = artistInput.nextSibling;
        while (typeSelect.tagName !== 'SELECT') {
         typeSelect = typeSelect.nextSibling;
        }
        artistInput.value = artistKey;
        var optionOffsets = yadgUtil.getOptionOffsets(typeSelect);
        if (artistType === 'main') {
         typeSelect.selectedIndex = optionOffsets[1];
        } else if (artistType === 'guest') {
         typeSelect.selectedIndex = optionOffsets[2];
        } else if (artistType === 'remixer') {
         typeSelect.selectedIndex = optionOffsets[3];
        } else {
         // we don't know this artist type, default to "main"
         typeSelect.selectedIndex = optionOffsets[1];
        }
        // next artist input
        inputIdx += 1;
       }
      }
      if (nullArtistCount > 0) {
       yadgUtil.addRemoveArtistBoxes(nullArtistCount *= -1);
      }
     }
     if (data.tags === false) {
      tagsInput.value = '';
     } else {
      tagsInput.value = data.tag_string.toLowerCase();
     }
     yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     yadgUtil.setValueIfSet(data.title, albumTitleInput, data.title !== false);
     yadgUtil.setValueIfSet(data.label, labelInput, data.label !== false);
     yadgUtil.setValueIfSet(data.catalog, catalogInput, data.catalog !== false);
    };
    return f;
   case 'waffles_upload':
    f = function (rawData) {
     var artistInput = document.getElementsByName('artist')[0];
     var albumTitleInput = document.getElementsByName('album')[0];
     var yearInput = document.getElementsByName('year')[0];
     var vaCheckbox = document.getElementById('va');
     var tagsInput = document.getElementById('tags');
     var data = yadg.prepareRawResponse(rawData);
     if (data.artists === false) {
      vaCheckbox.checked = false;
      artistInput.value = '';
     } else if (data.is_various) {
      artistInput.value = '';
      vaCheckbox.checked = true;
     } else {
      artistInput.value = data.flat_artistString;
      vaCheckbox.checked = false;
     }
     yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     yadgUtil.setValueIfSet(data.title, albumTitleInput, data.title !== false);
     if (data.tags === false) {
      tagsInput.value = '';
     } else {
      tagsInput.value = data.tag_string_nodots.toLowerCase();
     }
     yadgUtil.exec(function () {
      formatName();
     });
    };
    return f;
   case 'waffles_upload_new':
    f = function (rawData) {
     var artistInput = document.getElementById('id_artist');
     var albumTitleInput = document.getElementById('id_album');
     var yearInput = document.getElementById('id_year');
     var vaCheckbox = document.getElementById('id_va');
     var tagsInput = document.getElementById('id_tags');
     var data = yadg.prepareRawResponse(rawData);
     if (data.artists === false) {
      if (vaCheckbox.checked) {
       vaCheckbox.click();
      }
      artistInput.value = '';
     } else if (data.is_various) {
      if (!vaCheckbox.checked) {
       vaCheckbox.click();
      }
     } else {
      if (vaCheckbox.checked) {
       vaCheckbox.click();
      }
      artistInput.value = data.flat_artistString;
     }
     yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     yadgUtil.setValueIfSet(data.title, albumTitleInput, data.title !== false);
     if (data.tags === false) {
      tagsInput.value = '';
     } else {
      tagsInput.value = data.tag_string_nodots.toLowerCase();
     }
    };
    return f;
   case 'waffles_request':
    f = function (rawData) {
     var artistInput = document.getElementsByName('artist')[0];
     var albumTitleInput = document.getElementsByName('title')[0];
     var yearInput = document.getElementsByName('year')[0];
     var data = yadg.prepareRawResponse(rawData);
     if (data.artists === false) {
      artistInput.value = '';
     } else if (data.is_various) {
      artistInput.value = 'Various Artists';
     } else {
      artistInput.value = data.flat_artistString;
     }
     yadgUtil.setValueIfSet(data.year, yearInput, data.year !== false);
     yadgUtil.setValueIfSet(data.title, albumTitleInput, data.title !== false);
    };
    return f;
   default:
    // that should actually never happen
    return function () {};
  }
}
};
yadgTemplates = {
_templates: {},
_templateUrls: {},
getTemplate: function (id, callback) {
  if (id in this._templates) {
   callback(this._templates[id]);
  } else if (id in this._templateUrls) {
   var request = new requester(this._templateUrls[id], 'GET', function (template) { // eslint-disable-line new-cap
    yadgTemplates.addTemplate(template);
    callback(template);
   }, null, yadgTemplates.errorTemplate);
   request.send();
  } else {
   this.errorTemplate();
  }
},
addTemplate: function (template) {
  this._templates[template.id] = template;
},
addTemplateUrl: function (id, url) {
  this._templateUrls[id] = url;
},
errorTemplate: function () {
  yadg.printError('Could not get template. Please choose another one.', true);
}
};
yadgRenderer = {
_lastData: null,
_lastTemplateId: null,
render: function (templateId, data, callback, errorCallback) {
  this._lastData = data;
  var newTemplate = this._lastTemplateId !== templateId;
  this._lastTemplateId = templateId;
  yadgTemplates.getTemplate(templateId, function (template) {
   // the new template might have different dependencies, so initialize Swig with those
   if (newTemplate) {
    yadgSandbox.resetSandbox();
    yadgSandbox.initializeSwig(template.dependencies);
   }
   template.code = template.code.replace('https://what.cd', 'https://redacted.ch');
   yadgSandbox.renderTemplate(template.code, data, callback, errorCallback);
  });
},
renderCached: function (templateId, callback, errorCallback) {
  if (this.hasCached()) {
   this.render(templateId, this._lastData, callback, errorCallback);
  }
},
hasCached: function () {
  return this._lastData !== null;
},
clearCached: function () {
  this._lastData = null;
}
};
yadg = {
yadgHost: 'https://yadg.cc',
baseURI: '/api/v2/',
standardError: 'Sorry, an error occured. Please try again. If this error persists the user script might need updating.',
authenticationError: 'Your API token is invalid. Please provide a valid API token or remove the current one.',
lastStateError: false,
isBusy: false,
init: function () {
  this.scraperSelect = document.getElementById('yadg_scraper');
  this.formatSelect = document.getElementById('yadg_format');
  this.input = document.getElementById('yadg_input');
  this.targetSelect = document.getElementById('yadg_target');
  this.targetDescriptionSelect = document.getElementById('yadg_description_target');
  this.responseDiv = document.getElementById('yadg_response');
  this.button = document.getElementById('yadg_submit');
},
getBaseURL: function () {
  return this.yadgHost + this.baseURI;
},
getScraperList: function (callback) {
  var url = this.getBaseURL() + 'scrapers/';
  var request = new requester(url, 'GET', callback); // eslint-disable-line new-cap
  request.send();
},
getFormatsList: function (callback) {
  var url = this.getBaseURL() + 'templates/';
  this.getTemplates(url, [], callback);
},
getTemplates: function (url, templates, callback) {
  var request = new requester(url, 'GET', function (data) { // eslint-disable-line new-cap
   for (var i = 0; i < data.results.length; i++) {
    templates.push(data.results[i]);
   }
   if (data.next === null) {
    callback(templates);
   } else {
    yadg.getTemplates(data.next, templates, callback);
   }
  });
  request.send();
},
makeRequest: function (params) {
  if (this.isBusy) {
   return;
  }
  var data;
  if (params) {
   data = params;
  } else {
   data = {
    scraper: this.scraperSelect.options[this.scraperSelect.selectedIndex].value,
    input: this.input.value
   };
  }
  var url = this.getBaseURL() + 'query/';
  if (data.input !== '') {
   var request = new requester(url, 'POST', function (result) { // eslint-disable-line new-cap
    yadg.getResult(result.url);
   }, data);
   this.busyStart();
   request.send();
  }
},
getResult: function (resultUrl) {
  var request = new requester(resultUrl, 'GET', function (response) { // eslint-disable-line new-cap
   if (response.status === 'done') {
    if (response.data.type === 'ReleaseResult') {
     var templateId = yadg.formatSelect.options[yadg.formatSelect.selectedIndex].value;
     yadgRenderer.render(templateId, response, factory.setDescriptionBoxValue, factory.setDescriptionBoxValue);
     if (yadg.lastStateError === true) {
      yadg.responseDiv.innerHTML = '';
      yadg.lastStateError = false;
     }
     var fillFunc = factory.getFormFillFunction();
     fillFunc(response.data);
    } else if (response.data.type === 'ListResult') {
     var ul = document.createElement('ul');
     ul.id = 'yadg_release_list';
     var releaseList = response.data.items;
     for (var i = 0; i < releaseList.length; i++) {
      var name = releaseList[i].name;
      var info = releaseList[i].info;
      var queryParams = releaseList[i].queryParams;
      var releaseUrl = releaseList[i].url;
      var li = document.createElement('li');
      var a = document.createElement('a');
      a.textContent = name;
      a.params = queryParams;
      a.href = releaseUrl;
      a.addEventListener('click', function (e) {
       e.preventDefault();
       yadg.makeRequest(this.params);
       if (factory.getFetchImageCheckbox().checked) {
        fetchImage(this.href, function (data) {
         insertImage(data, function () {
          if (factory.getAutoRehostCheckbox() && factory.getAutoRehostCheckbox().checked) {
           pthImgIt();
          }
         });
        });
       }
      }, false);
      li.appendChild(a);
      li.appendChild(document.createElement('br'));
      li.appendChild(document.createTextNode(info));
      ul.appendChild(li);
     }
     if (ul.childNodes.length === 0) {
      yadg.printError('Sorry, there were no matches.');
     } else {
      yadg.responseDiv.innerHTML = '';
      yadg.responseDiv.appendChild(ul);
      yadg.lastStateError = false;
      // we got a ListResult so clear the last ReleaseResult from the render cache
      yadgRenderer.clearCached();
     }
    } else if (response.data.type === 'NotFoundResult') {
     yadg.printError('I could not find the release with the given ID. You may want to try again with another one.');
    } else {
     yadg.printError('Something weird happened. Please try again');
    }
    yadg.busyStop();
   } else if (response.status === 'failed') {
    yadg.failedCallback();
   } else {
    var delay = function () {
     yadg.getResult(response.url);
    };
    window.setTimeout(delay, 1000);
   }
  });
  request.send();
},
printError: function (message, templateError) {
  this.responseDiv.innerHTML = '';
  this.responseDiv.appendChild(document.createTextNode(message));
  if (!templateError) {
   this.lastStateError = true;
   // there was a non template related error, so for consistencies sake clear the last ReleaseResult from the
   // render cache
   yadgRenderer.clearCached();
  }
},
failedCallback: function () {
  yadg.printError(yadg.standardError);
  yadg.busyStop();
},
failedAuthenticationCallback: function () {
  yadg.printError(yadg.authenticationError);
  yadg.busyStop();
},
busyStart: function () {
  this.isBusy = true;
  this.button.setAttribute('disabled', true);
  this.button.value = 'Please wait...';
  this.input.setAttribute('disabled', true);
  this.scraperSelect.setAttribute('disabled', true);
  this.formatSelect.setAttribute('disabled', true);
  this.targetSelect.setAttribute('disabled', true);
},
busyStop: function () {
  this.button.removeAttribute('disabled');
  this.button.value = 'Fetch';
  this.input.removeAttribute('disabled');
  this.scraperSelect.removeAttribute('disabled');
  this.formatSelect.removeAttribute('disabled');
  this.targetSelect.removeAttribute('disabled');
  this.isBusy = false;
},
prepareRawResponse: function (rawData) { // eslint-disable-line complexity
  var result = {};
  result.artists = false;
  result.year = false;
  result.title = false;
  result.label = false;
  result.catalog = false;
  result.genre = false;
  result.style = false;
  result.tags = false;
  result.is_various = false; // eslint-disable-line camelcase
  result.flat_artistString = false; // eslint-disable-line camelcase
  if (rawData.artists.length > 0) {
   result.artists = {};
   for (var i = 0; i < rawData.artists.length; i++) {
    var artist = rawData.artists[i];
    if (artist.isVarious) {
     result.is_various = true; // eslint-disable-line camelcase
    } else {
     result.artists[artist.name] = artist.types;
    }
   }
  }
  if (rawData.discs.length > 0) {
   for (var k = 0; k < rawData.discs.length; k++) {
    var disc = rawData.discs[k];
    for (var l = 0; l < disc.tracks.length; l++) {
     var track = disc.tracks[l];
     for (var m = 0; m < track.artists.length; m++) {
      var name = track.artists[m].name;
      var type = track.artists[m].types;
      var newTypes = null;
      if (name in result.artists) {
       newTypes = result.artists[name].concat(type);
       // deduplicate new types array
       for (i = 0; i < newTypes.length; ++i) {
        for (var j = i + 1; j < newTypes.length; ++j) {
         if (newTypes[i] === newTypes[j]) {
          newTypes.splice(j--, 1);
         }
        }
       }
      } else {
       newTypes = type;
      }
      result.artists[name] = newTypes;
     }
    }
   }
  }
  for (i = 0; i < rawData.releaseEvents.length; i++) {
   var event = rawData.releaseEvents[i];
   if (event.date) {
    result.year = event.date.match(/\d{4}/)[0];
    if (result.year.length === 4) {
     break;
    } else {
     result.year = false;
    }
   }
  }
  if (rawData.title) {
   result.title = rawData.title;
  }
  if (rawData.labelIds.length > 0) {
   var labelId = rawData.labelIds[0];
   if (labelId.label) {
    result.label = labelId.label;
   }
   if (labelId.catalogueNrs.length > 0) {
    result.catalog = labelId.catalogueNrs[0];
   }
  }
  if (rawData.genres.length > 0) {
   result.genre = rawData.genres;
  }
  if (rawData.styles.length > 0) {
   result.style = rawData.styles;
  }
  if (result.genre !== false && result.style !== false) {
   result.tags = rawData.genres.concat(rawData.styles);
  } else if (result.genre !== false) {
   result.tags = rawData.genres;
  } else if (result.style !== false) {
   result.tags = rawData.styles;
  }
  if (result.tags !== false) {
   result.tag_string = ''; // eslint-disable-line camelcase
   result.tag_string_nodots = ''; // eslint-disable-line camelcase
   for (i = 0; i < result.tags.length; i++) {
    result.tag_string += result.tags[i].replace(/\s+/g, '.'); // eslint-disable-line camelcase
    result.tag_string_nodots += result.tags[i].replace(/\s+/g, ' '); // eslint-disable-line camelcase
    if (i !== result.tags.length - 1) {
     result.tag_string += ', '; // eslint-disable-line camelcase
     result.tag_string_nodots += ', '; // eslint-disable-line camelcase
    }
   }
  }
  if (result.artists !== false) {
   // count the artists
   result.artists_length = 0; // eslint-disable-line camelcase
   result.artist_keys = []; // eslint-disable-line camelcase
   result.effective_artist_count = 0; // eslint-disable-line camelcase
   for (i in result.artists) {
    if ({}.hasOwnProperty.call(result.artists, i)) {
     result.artists_length++;
     result.artist_keys.push(i);
     result.effective_artist_count += result.artists[i].length; // eslint-disable-line camelcase
    }
   }
  }
  if (result.artists_length === 0) {
   result.artists = false;
  } else {
   // create a flat string of all the main artists
   var artistString = '';
   for (i = 0; i < result.artists_length; i++) {
    if (result.artists[result.artist_keys[i]].indexOf('main') !== -1) {
     if (artistString !== '' && i < result.artists_length - 2) {
      artistString += ', ';
     } else if (artistString !== '' && i < result.artists_length - 1) {
      artistString += ' & ';
     }
     artistString += result.artist_keys[i];
    }
   }
   result.flat_artistString = artistString; // eslint-disable-line camelcase
  }
  return result;
}
};
yadgSandbox.init(function () {
if (factory.init()) { // returns true if we run on a valid location
  yadg.init();
}
});
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 22-Ноя-17 00:27 (спустя 4 часа)

maximus_lt
Сейчас есть время только на свои скрипты, а этот на 2000+ строк не потяну, к тому же нет ни Violentmonkey, ни Maxthon.
[Профиль]  [ЛС] 

RoxMarty

RG Мультфильмы

Стаж: 17 лет 7 месяцев

Сообщений: 14622

RoxMarty · 22-Ноя-17 00:51 (спустя 23 мин., ред. 22-Ноя-17 00:51)

unchqua
Спасибо за труды. Всё нужное отлично работает!
P.S. Правда сейчас из 12 обновлённых тем (вкладок) - две открылись совершенно пустыми и даже обновление страницы не помогает. Возможно баг браузера... буду тестить дальше
[Профиль]  [ЛС] 

maximus_lt

Moderator gray

Стаж: 17 лет 5 месяцев

Сообщений: 6144

maximus_lt · 22-Ноя-17 03:43 (спустя 2 часа 52 мин.)

unchqua, будем надеяться, что решат проблему, т.к. скрипт BBCode, Gazelle Snatched работает, а этот отказался.
[Профиль]  [ЛС] 

megane68

Стаж: 16 лет 6 месяцев

Сообщений: 19966

megane68 · 14-Дек-17 06:27 (спустя 22 дня)

Внесу свою лепту.
https://userstyles.org/styles/72176/rutracker-org-top-fix-for-firefox
[Профиль]  [ЛС] 

Аndy

Moderator senior

Стаж: 16 лет 3 месяца

Сообщений: 52867

Аndy · 14-Дек-17 07:48 (спустя 1 час 21 мин.)

Под Palemoon 27.6.2x64 на 3.30rc3 YADG перестал работать (
[Профиль]  [ЛС] 

maximus_lt

Moderator gray

Стаж: 17 лет 5 месяцев

Сообщений: 6144

maximus_lt · 30-Дек-17 21:54 (спустя 16 дней)

unchqua писал(а):
74251009Рутрекер 21. Чёрный список входящих сообщений ЛС. Либо скрытие и удаление, либо только скрытие. Подробнее.
скрытый текст
Код:
// ==UserScript==
// @name           Рутрекер 21. Чёрный список входящих сообщений ЛС.
// @namespace      http://www.unchqua.ru/ns/greasemonkey
// @include        *://rutr.life/forum/*
// @include        *://rutracker.net/forum/*
// @include        *://rutracker.cr/forum/*
// @include        *://rutracker.nl/forum/*
// @require        https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
// @version        1
// @grant          none
// ==/UserScript==
(function(){
var $ = jQuery.noConflict(true);
// Заполнение элемента списка фильтруемых пользователей.
var update_list = function(blacklisted) {
var list = $("#rto21-list");
list.empty();
for (idx = 0; idx < blacklisted.length; idx++) {
  var el_option = $("<option>");
  el_option.text(blacklisted[idx]["nick"]+" ("+blacklisted[idx]["id"]+")");
  el_option.val(blacklisted[idx]["id"]);
  list.append(el_option);
}
$("#rto21-count").text(blacklisted.length);
};
// Мы на странице профиля - ведём список пользователей.
if (window.location.pathname === "/forum/profile.php") {
do {
// Если это не мой профиль, не работаем.
if ($("#main_content_wrap > h1").text().indexOf("Мой профиль") === -1)
break;
// Берём список заблокированных из хранилища.
var blacklisted_users = localStorage.getItem("rto21_blacklisted") || "[]";
blacklisted_users = JSON.parse(blacklisted_users);
// Элемент списка фильтруемых пользователей.
var list = $("<select id=\"rto21-list\" multiple=\"multiple\" size=\"11\" style=\"width: 20em; float: left; margin-right: .5em; height: 17em;\">");
// Галочка включения-выключения фильтрации.
var enabled_checkbox = $("<input type=\"checkbox\" id=\"rto21-enabled\">").click(function(){
localStorage.setItem("rto21_enabled", this.checked);
});
enabled_checkbox
.css({"margin-left":".5em","margin-right":".5em"})
.prop("checked", localStorage.getItem("rto21_enabled") === "true");
// Переключатель способа работы: удаление сообщений или их сокрытие. По умолчанию - удаление.
if (!localStorage.getItem("rto21_method"))
localStorage.setItem("rto21_method", "delete");
var method_delete_radio = $("<input type=\"radio\" id=\"rto21-method-delete\" name=\"rto21-method\" value=\"delete\"/>").click(function(){
localStorage.setItem("rto21_method", "delete");
});
method_delete_radio
.css({"margin":".5em .5em 0 0","vertical-align":"top"})
.prop("checked", localStorage.getItem("rto21_method") === "delete");
var method_hide_radio = $("<input type=\"radio\" id=\"rto21-method-hide\" name=\"rto21-method\" value=\"hide\"/>").click(function(){
localStorage.setItem("rto21_method", "hide");
});
method_hide_radio
.css({"margin":".5em .5em 0 2em","vertical-align":"top"})
.prop("checked", localStorage.getItem("rto21_method") === "hide");
// Надпись с количеством записей в списке.
var count_text = $("<span>(<span id=\"rto21-count\"></span>)</span>").css({"margin-left":".5em","margin-right":".5em"});
// Добавляем список в интерфейс.
var container = $("table.user_profile > tbody > tr:eq(1) > td:eq(1)");
container.append($(
  "<fieldset id=\"rto21-container\" style=\"margin: 0 8px 3px;\">" +
  "<legend>Чёрный список входящих ЛС</legend>" +
  "<div style=\"padding: 4px;\"></div>" +
  "</fieldset>"
));
container.find("#rto21-container div").append(list);
container.find("#rto21-container legend").prepend(enabled_checkbox).append(count_text);
// Кнопка добавления записи.
var add_button = $("<input type=\"button\">").val("Добавить").click(function(){
var el_newusernick_text = $("#rto21-newusernick");
var newuser_nick = el_newusernick_text.val();
var el_newuserid_text = $("#rto21-newuserid");
var newuser_id = parseInt(el_newuserid_text.val());
// Правильно ли введён id пользователя.
if (isNaN(newuser_id)) {
  el_newuserid_text.css({
   "background-color": "#FF7F7F"
  }).delay(400).queue(function(){
   $(this).css({
    "background-color": "white"
   }).dequeue();
  });
  return;
}
// Если логин не задан, составляем его сами.
newuser_id = parseInt(newuser_id);
if (newuser_nick.length === 0) {
  newuser_nick = "Пользователь " + newuser_id;
}
// Ищем, нет ли уже такого пользователя в списке.
var found = blacklisted_users.findIndex(function(user){
  return newuser_id === user["id"];
});
// Если такой есть, не работаем.
if (found >= 0) {
  el_newuserid_text.css({
   "background-color": "#FF7F7F"
  }).delay(400).queue(function(){
   $(this).css({
    "background-color": "white"
   }).dequeue();
  });
  return;
}
// Ищем место, куда добавить новую запись. Записи должны быть отсортированы по алфавиту.
for (var idx = 0; idx < blacklisted_users.length; idx++) {
  if (blacklisted_users[idx]["nick"].toUpperCase() > newuser_nick.toUpperCase())
   break;
}
// Добавляем в начало списка.
if (idx === 0) {
  blacklisted_users.unshift({"nick":newuser_nick,"id":newuser_id});
}
// Добавляем в конец списка.
else if (idx >= blacklisted_users.length) {
  blacklisted_users.push({"nick":newuser_nick,"id":newuser_id});
}
// Добавляем в середину списка.
else {
  for (var idx1 = blacklisted_users.length-1; idx1 >= idx; idx1--) {
   blacklisted_users[idx1+1] = blacklisted_users[idx1];
  }
  blacklisted_users[idx] = {"nick":newuser_nick,"id":newuser_id};
}
// Обновляем список.
update_list(blacklisted_users);
// Очищаем поля ввода id и логина пользователя.
el_newusernick_text.val("");
el_newuserid_text.val("");
// Сохраняем список в хранилище.
localStorage.setItem("rto21_blacklisted", JSON.stringify(blacklisted_users));
});
// Кнопка удаления записи.
var remove_button = $("<input type=\"button\">").val("Удалить").click(function(){
var el_list = $("#rto21-list");
// Если ничего не выбрано, не работаем.
if (el_list.find("option:selected").length === -1)
  return;
// Удаляем выбранное из списка.
blacklisted_users = blacklisted_users.filter(function(user){
  return el_list.find("option[value='"+user["id"]+"']:selected").length === 0;
});
// Обновляем список.
update_list(blacklisted_users);
// Сохраняем список в хранилище.
localStorage.setItem("rto21_blacklisted", JSON.stringify(blacklisted_users));
});
// Добавляем элементы управления списком.
container.find("#rto21-container div").append(
$("<p style=\"margin-bottom: 2em;\">")
     .append("<p style=\"font-size: smaller;\"><span style=\"font-weight: bold;\">Для добавления записи</span> введите логин пользователя и его цифровой id и нажмите на кнопку.<br/>Логин не обязателен, поиск будет производиться по Id.</p>")
     .append("<input type=\"text\" id=\"rto21-newusernick\" placeholder=\"Логин\" size=\"12\" style=\"margin-right: .5em;\"/>")
     .append("<input type=\"text\" id=\"rto21-newuserid\" placeholder=\"Id\" size=\"8\" style=\"margin-right: .5em;\"/>")
     .append(add_button),
$("<p style=\"margin-bottom: 2em;\">")
     .append("<p style=\"font-size: smaller;\"><span style=\"font-weight: bold;\">Для удаления записей</span> выберите одну или несколько строк и нажмите на кнопку.<br/>Несколько строк можно выбрать с помощью Ctrl или Shift.</p>")
     .append(remove_button),
$("<p style=\"margin-bottom: 2em;\">")
     .append("<p>Способ действия:</p>")
     .append(method_delete_radio, $("<label for=\"rto21-method-delete\" style=\"vertical-align: top;\"><p>Удаление<br/><span style=\"font-size: smaller;\">Скрывать, в фоне <span style=\"font-weight: bold;\">удалять</span>.</span></p></label>"))
     .append(method_hide_radio, $("<label for=\"rto21-method-hide\" style=\"vertical-align: top;\"><p>Сокрытие<br/><span style=\"font-size: smaller;\">Только <span style=\"font-weight: bold;\">скрывать</span>.</span></p></label>"))
);
// Заполняем список значениями.
update_list(blacklisted_users);
} while (false);
} // Страница своего профиля.
// Мы на странице входящих ЛС - фильтруем пользователей.
if (window.location.pathname === "/forum/privmsg.php" && window.location.search.indexOf("?folder=inbox") === 0) {
do {
// Если фильтрация не включена, не работаем.
var enabled = localStorage.getItem("rto21_enabled") === "true";
if (!enabled)
break;
// Способ действия.
var method = localStorage.getItem("rto21_method") || "delete";
// Берём список заблокированных из хранилища.
var blacklisted_users = localStorage.getItem("rto21_blacklisted") || "[]";
blacklisted_users = JSON.parse(blacklisted_users);
// Если списка нет, нечего фильтровать, выходим.
if (blacklisted_users.length === 0)
break;
// Ищем фильтруемых пользователей и запоминаем id их сообщений.
var to_delete = "";
$("table.forumline td.pm-nick-td a.med").filter(function(idx,elem){
var el_nick = $(elem);
var user_id = parseInt(el_nick.attr("href").replace(/.+&u=(\d+)$/, "$1"));
var found = blacklisted_users.findIndex(function(blacklisted){
  return user_id === blacklisted["id"];
});
return found > -1;
}).each(function(idx, elem){
var el_nick = $(elem);
var el_message_tr = el_nick.parentsUntil("tr").parent();
el_message_tr.hide();
var message_id = parseInt(el_message_tr.attr("id").replace(/^tr-(\d+)$/, "$1"));
to_delete += "&mark%5B"+idx+"%5D="+message_id;
});
// Если удалять нечего, выходим.
if (to_delete.length === 0)
break;
// Удаление сообщений, если выбран такой способ работы.
if (method === "delete")
$.post({
  url: "//" + window.location.hostname + "/forum/privmsg.php?folder=inbox",
  data: "mode=&delete=1"+to_delete+"&confirm=%C4%E0&form_token="+window.BB.form_token,
  success: function (data, status) {
   //console.log("Удалено сообщение "+(idx+1)+ " (id="+message_id+")");
   //window.location.reload();
  }
  , async: false
});
} while (false);
} // Страница входящих ЛС.
})();
Надо доработать скрипт: он основан на удалении сообщений, т.е. по факту полученных. Надо сделать именно запрет, что бы отправитель получал сообщение аналогичное общим настройкам - "Пользователь отключил получение сообщений" либо "Вам запрещено писать пользователю".
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 30-Дек-17 22:07 (спустя 12 мин.)

maximus_lt
Мой скрипт удаляет сообщения не читая, то есть у отправителя не обозначается, что они открывались.
А эти перечисленные настройки серверные, со стороны клиента на них нельзя повлиять.
[Профиль]  [ЛС] 

maximus_lt

Moderator gray

Стаж: 17 лет 5 месяцев

Сообщений: 6144

maximus_lt · 30-Дек-17 22:14 (спустя 6 мин.)

unchqua, тогда можно эстетики ради сделать, что бы настройки "Чёрный список входящих ЛС" в профиле можно было свернуть ?
[Профиль]  [ЛС] 

unchqua

Техническая помощь (неактивен)

Стаж: 16 лет 4 месяца

Сообщений: 1060

unchqua · 30-Дек-17 22:18 (спустя 4 мин.)

maximus_lt
Если вообще не хотите им пользоваться, скрипт можно отключить в Greasemonkey. А окно со списком да, могу сделать поменьше по высоте.
[Профиль]  [ЛС] 
 
Ответить
Loading...
Error