BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

global.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. "use strict";
  2. /**
  3. * html_entity_decode
  4. */
  5. function html_entity_decode(str) {
  6. var el = document.createElement("div");
  7. el.innerHTML = str;
  8. for (var i = 0, ret = ''; i < el.childNodes.length; i++) {
  9. ret += el.childNodes[i].nodeValue;
  10. }
  11. return ret;
  12. }
  13. /**
  14. * get_size
  15. */
  16. function get_size(size) {
  17. var steps = 0;
  18. for (; size >= 1024; size /= 1024, steps++);
  19. var exts = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
  20. return (size.toFixed(2) + (exts[steps] || ''))
  21. }
  22. /**
  23. * ratio
  24. */
  25. function ratio(a, b) {
  26. var rc = 'r50';
  27. for (var i of [[5, '20'], [2, '10'], [1, '09'], [0.9, '08'], [0.8, '07'], [0.7, '06'],
  28. [0.6, '05'], [0.5, '04'], [0.4, '03'], [0.3, '02'], [0.2, '01'], [0.1, '00']]) {
  29. if (a / b < i[0]) rc = 'r' + i[1];
  30. }
  31. if (b == 0) return a ? '<span class="r99">∞</span>' : '--';
  32. return '<span class="' + rc + '">' + ((a / b) - 0.005).toFixed(2) + '</span>';
  33. }
  34. /**
  35. * save_message
  36. */
  37. function save_message(message, err = false) {
  38. var messageDiv = document.createElement("div");
  39. messageDiv.className = err ? "error_message box" : "save_message box";
  40. messageDiv.innerHTML = message;
  41. $("#content").raw().insertBefore(messageDiv, $("#content").raw().firstChild);
  42. }
  43. $.fn.extend({
  44. gshow: function () {
  45. return this.remove_class('hidden');
  46. },
  47. ghide: function (force) {
  48. return this.add_class('hidden', force);
  49. },
  50. gtoggle: function (force) {
  51. if (this[0].className.split(' ').indexOf('hidden') == -1) {
  52. this.add_class('hidden', force);
  53. } else {
  54. this.remove_class('hidden');
  55. }
  56. return this;
  57. },
  58. listen: function (event, callback) {
  59. for (var i = 0; i < this.length; i++) {
  60. var object = this[i];
  61. if (document.addEventListener) {
  62. object.addEventListener(event, callback, false);
  63. } else {
  64. object.attachEvent('on' + event, callback);
  65. }
  66. }
  67. return this;
  68. },
  69. add_class: function (class_name, force) {
  70. for (var i = 0; i < this.length; i++) {
  71. var object = this[i];
  72. if (object.className === '') {
  73. object.className = class_name;
  74. } else if (force || object.className.split(' ').indexOf(class_name) == -1) {
  75. object.className = object.className + ' ' + class_name;
  76. }
  77. }
  78. return this;
  79. },
  80. remove_class: function (class_name) {
  81. for (var i = 0; i < this.length; i++) {
  82. var object = this[i];
  83. var classes = object.className.split(' ');
  84. var result = classes.indexOf(class_name);
  85. if (result != -1) {
  86. classes.splice(result, 1);
  87. object.className = classes.join(' ');
  88. }
  89. }
  90. return this;
  91. },
  92. has_class: function (class_name) {
  93. for (var i = 0; i < this.length; i++) {
  94. var object = this[i];
  95. var classes = object.className.split(' ');
  96. if (classes.indexOf(class_name) != -1) {
  97. return true;
  98. }
  99. }
  100. return false;
  101. },
  102. toggle_class: function (class_name) {
  103. for (var i = 0, il; i < this.length; i++) {
  104. var object = this[i];
  105. var classes = object.className.split(' ');
  106. var result = classes.indexOf(class_name);
  107. if (result != -1) {
  108. classes.splice(result, 1);
  109. object.className = classes.join(' ');
  110. } else {
  111. if (object.className === '') {
  112. object.className = class_name;
  113. } else {
  114. object.className = object.className + ' ' + class_name;
  115. }
  116. }
  117. }
  118. return this;
  119. },
  120. disable: function () {
  121. $(this).prop('disabled', true);
  122. return this;
  123. },
  124. enable: function () {
  125. $(this).prop('disabled', false);
  126. return this;
  127. },
  128. raw: function (number) {
  129. if (typeof number == 'undefined') {
  130. number = 0;
  131. }
  132. return $(this).get(number);
  133. },
  134. nextElementSibling: function () {
  135. var here = this[0];
  136. if (here.nextElementSibling) {
  137. return $(here.nextElementSibling);
  138. }
  139. do {
  140. here = here.nextSibling;
  141. } while (here.nodeType != 1);
  142. return $(here);
  143. },
  144. previousElementSibling: function () {
  145. var here = this[0];
  146. if (here.previousElementSibling) {
  147. return $(here.previousElementSibling);
  148. }
  149. do {
  150. here = here.nextSibling;
  151. } while (here.nodeType != 1);
  152. return $(here);
  153. },
  154. // Disable unset form elements to allow search URLs cleanups
  155. disableUnset: function () {
  156. $('input, select', this).filter(function () {
  157. return $(this).val() === "";
  158. }).disable();
  159. return this;
  160. },
  161. // Prevent double submission of forms
  162. preventDoubleSubmission: function () {
  163. $(this).submit(function (e) {
  164. var $form = $(this);
  165. if ($form.data('submitted') === true) {
  166. e.preventDefault();
  167. } else {
  168. $form.data('submitted', true);
  169. }
  170. });
  171. return this;
  172. }
  173. });
  174. if ($('meta[name=authkey]').raw()) {
  175. var authkey = $('meta[name=authkey]').raw().content;
  176. var userid = parseInt($('meta[name=userid]').raw().content);
  177. }
  178. /**
  179. * Check or uncheck checkboxes in formElem
  180. * If masterElem is false, toggle each box, otherwise use masterElem's status on all boxes
  181. * If elemSelector is false, act on all checkboxes in formElem
  182. */
  183. function toggleChecks(formElem, masterElem, elemSelector) {
  184. elemSelector = elemSelector || 'input:checkbox';
  185. if (masterElem) {
  186. $('#' + formElem + ' ' + elemSelector).prop('checked', masterElem.checked);
  187. } else {
  188. $('#' + formElem + ' ' + elemSelector).each(function () {
  189. this.checked = !this.checked;
  190. })
  191. }
  192. }
  193. var lightbox = {
  194. init: function (image, size) {
  195. if ($('#lightbox').length == 0 || $('#curtain').length == 0) {
  196. var lightboxEl = document.createElement('div')
  197. lightboxEl.id = 'lightbox'
  198. lightboxEl.className = 'lightbox hidden'
  199. var curtainEl = document.createElement('div')
  200. curtainEl.id = 'curtain'
  201. curtainEl.className = 'curtain hidden'
  202. $('#wrapper')[0].appendChild(lightboxEl)
  203. $('#wrapper')[0].appendChild(curtainEl)
  204. }
  205. if (typeof (image) == 'string') {
  206. $('#lightbox').gshow().listen('click', lightbox.unbox).raw().innerHTML =
  207. '<p size="7" style="color: gray; font-size: 50px;">Loading...<p>';
  208. $('#curtain').gshow().listen('click', lightbox.unbox);
  209. var src = image;
  210. image = new Image();
  211. image.onload = function () {
  212. lightbox.box_async(image);
  213. }
  214. image.src = src;
  215. }
  216. if (image.naturalWidth === undefined) {
  217. var tmp = document.createElement('img');
  218. tmp.style.visibility = 'hidden';
  219. tmp.src = image.src;
  220. image.naturalWidth = tmp.width;
  221. }
  222. if (image.naturalWidth > size) {
  223. lightbox.box(image);
  224. }
  225. },
  226. box: function (image) {
  227. var hasA = false;
  228. if (image.parentNode != null && image.parentNode.tagName.toUpperCase() == 'A') {
  229. hasA = true;
  230. }
  231. if (!hasA) {
  232. $('#lightbox').gshow().listen('click', lightbox.unbox).raw().innerHTML = '<img src="' + image.src + '" alt="" />';
  233. $('#curtain').gshow().listen('click', lightbox.unbox);
  234. }
  235. },
  236. box_async: function (image) {
  237. var hasA = false;
  238. if (image.parentNode != null && image.parentNode.tagName.toUpperCase() == 'A') {
  239. hasA = true;
  240. }
  241. if (!hasA) {
  242. $('#lightbox').raw().innerHTML = '<img src="' + image.src + '" alt="" />';
  243. }
  244. },
  245. unbox: function (data) {
  246. $('#curtain').ghide();
  247. $('#lightbox').ghide().raw().innerHTML = '';
  248. }
  249. };
  250. // Horrible hack to let arrow keys work as forward/back in lightbox
  251. window.onkeydown = function (e) {
  252. e = e || window.event
  253. if (e.keyCode == 37 || e.keyCode == 39) {
  254. if ($('#lightbox').raw() && !$('#lightbox').raw().classList.contains('hidden')) {
  255. ($('[id!="lightbox"] > [lightbox-img="' + $('#lightbox > img').raw().src + '"]').raw()[((e.keyCode == 39) ? 'next' : 'previous') + 'Sibling'].click() || function () { })()
  256. }
  257. }
  258. }
  259. function resize(id) {
  260. var textarea = document.getElementById(id);
  261. if (textarea.scrollHeight > textarea.clientHeight) {
  262. textarea.style.height = Math.min(1000, textarea.scrollHeight + textarea.style.fontSize) + 'px';
  263. }
  264. }
  265. //ZIP downloader stuff
  266. function add_selection() {
  267. var selected = $('#formats').raw().options[$('#formats').raw().selectedIndex];
  268. if (selected.disabled === false) {
  269. var listitem = document.createElement("li");
  270. listitem.id = 'list' + selected.value;
  271. listitem.innerHTML = ' <input type="hidden" name="list[]" value="' + selected.value + '" /> ' +
  272. ' <span style="float: left;">' + selected.innerHTML + '</span>' +
  273. ' <a href="#" onclick="remove_selection(\'' + selected.value + '\'); return false;" class="float_right" class="brackets">X</a>' +
  274. ' <br style="clear: all;" />';
  275. $('#list').raw().appendChild(listitem);
  276. $('#opt' + selected.value).raw().disabled = true;
  277. }
  278. }
  279. function remove_selection(index) {
  280. $('#list' + index).remove();
  281. $('#opt' + index).raw().disabled = '';
  282. }
  283. function preload(image) {
  284. var img = document.createElement('img')
  285. img.style.display = 'none'
  286. img.src = image
  287. document.body.appendChild(img)
  288. document.body.removeChild(img)
  289. }
  290. let coverListener
  291. function getCover(event) {
  292. let image = event.target.attributes['data-cover'].value
  293. $('#coverCont img').remove()
  294. let coverCont = ($('#coverCont').length == 0) ? document.body.appendChild(document.createElement('div')) : $('#coverCont')[0]
  295. coverCont.id = 'coverCont'
  296. if ($('#coverCont img').length == 0) {
  297. coverCont.appendChild(document.createElement('img'))
  298. }
  299. $('#coverCont img')[0].src = image ? image : '/static/common/noartwork/music.png'
  300. coverCont.style.display = 'block'
  301. coverListener = mevent => {
  302. let wh = window.innerHeight, ch = coverCont.clientHeight, ph = mevent.clientY
  303. let pos = (ph < wh / 2) ? ((ph + ch + 10 > wh) ? wh - ch : ph + 10) : ((ph - ch - 10 < 0) ? 0 : ph - ch - 10)
  304. coverCont.style.top = pos + 'px'
  305. if (mevent.clientX > window.innerWidth / 2) {
  306. coverCont.style.left = "initial"
  307. coverCont.style.right = window.innerWidth - mevent.clientX + 10 + "px"
  308. } else {
  309. coverCont.style.left = mevent.clientX + 10 + "px"
  310. coverCont.style.right = "initial"
  311. }
  312. }
  313. document.addEventListener("mousemove", coverListener)
  314. //Preload next image
  315. if ($('.torrent_table, .request_table').length > 0) {
  316. var as = $('[data-cover]')
  317. var a = event.target
  318. preload((as[as.toArray().indexOf(a) + 1] || as[0]).attributes['data-cover'].value)
  319. preload((as[as.toArray().indexOf(a) - 1] || as[0]).attributes['data-cover'].value)
  320. }
  321. }
  322. function ungetCover(event) {
  323. $('#coverCont img').remove()
  324. coverCont.style.display = 'none'
  325. document.removeEventListener("mousemove", coverListener)
  326. }
  327. // Apparently firefox doesn't implement NodeList.forEach until FF50
  328. // Remove this shim after that's stable for a while
  329. if (typeof NodeList.prototype.forEach !== 'function') {
  330. NodeList.prototype.forEach = Array.prototype.forEach
  331. }
  332. $(function () {
  333. document.querySelectorAll('[data-toggle-target]').forEach(function (el) {
  334. el.addEventListener('click', function (event) {
  335. $(el.attributes['data-toggle-target'].value).gtoggle()
  336. if (el.attributes['data-toggle-replace']) {
  337. [el.innerHTML, el.attributes['data-toggle-replace'].value] = [el.attributes['data-toggle-replace'].value, el.innerHTML]
  338. }
  339. })
  340. })
  341. $(document).on('mouseover', '[data-cover]', getCover)
  342. $(document).on('mouseleave', '[data-cover]', ungetCover)
  343. $(document).on('click', '.lightbox-init', function (e) {
  344. lightbox.init((e.target.attributes['lightbox-img'] || []).value || e.target.src, (e.target.attributes['lightbox-size'] || []).value || e.target.width)
  345. })
  346. })
  347. /**
  348. * jQuery tooltips
  349. * Replaces Tooltipster
  350. * @see https://jqueryui.com/tooltip/
  351. */
  352. $(function () {
  353. $(document).tooltip();
  354. });