Oppaitime'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

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