main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. 'use strict';
  2. var OSGC = window.OSGC = {};
  3. var params = window.params = {};
  4. var activeTag = window.activeTag = null;
  5. // Lazy load badges when they become visible (avoid error 429)
  6. function lazyloadHandler(e) {
  7. var elements = document.querySelectorAll("img.lazyload");
  8. for (var i = 0; i < elements.length; i++) {
  9. var boundingClientRect = elements[i].getBoundingClientRect();
  10. if (
  11. elements[i].hasAttribute("data-src") &&
  12. boundingClientRect.top < window.innerHeight &&
  13. // Ensure parent game is not hidden
  14. elements[i].offsetParent != null
  15. ) {
  16. elements[i].setAttribute("src", elements[i].getAttribute("data-src"));
  17. elements[i].removeAttribute("data-src");
  18. }
  19. }
  20. }
  21. function handleContentChanged() {
  22. setCount();
  23. lazyloadHandler();
  24. }
  25. // menu
  26. (function() {
  27. const nav = document.getElementById('nav');
  28. const btns = Array.from(document.getElementsByClassName('nav-btn'));
  29. nav.addEventListener('click', menuclick);
  30. function menuclick(ev) {
  31. const t = ev.target;
  32. if (t.classList.contains('nav-btn')) {
  33. const wasActive = t.parentNode.classList.contains('active');
  34. btns.forEach((btn) => btn.parentNode.classList.remove('active'));
  35. if (!wasActive) { t.parentNode.classList.add('active') };
  36. }
  37. }
  38. })();
  39. // gallery handling
  40. (function() {
  41. var toggleGallery = function(t) {
  42. var gallery = t.parentNode.getElementsByClassName('gallery')[0];
  43. var raw = t.parentNode.getElementsByClassName('gallery-raw')[0];
  44. var show_now = gallery.style.display == 'none';
  45. t.innerHTML = show_now ? '&#x25bc;' : '&#x25b6;';
  46. gallery.style.display = show_now ? 'block' : 'none';
  47. if (raw && show_now) { gallery.insertAdjacentHTML('beforeend', raw.innerHTML); }
  48. else { gallery.innerHTML = ''; }
  49. };
  50. document.body.addEventListener('click', function(ev) {
  51. if (!ev.target || !ev.target.matches("span.toggler")) return;
  52. toggleGallery(ev.target);
  53. })
  54. var visibleTogglers = document.querySelectorAll('span.toggler.visible');
  55. for (var i = 0; i < visibleTogglers.length; i++) {
  56. toggleGallery(visibleTogglers[i]);
  57. }
  58. })();
  59. // search handling
  60. function getFilter(term) {
  61. return !term ? "" :
  62. '[data-index*="' + term.toLowerCase().replace(/"/g, '') + '"]';
  63. }
  64. var filterStyle = document.getElementById('filter-style');
  65. function filter(filter_value) {
  66. if (!filter_value) {
  67. setQueryParams('filter', null);
  68. filterStyle.innerHTML = "";
  69. } else {
  70. setQueryParams('filter', filter_value);
  71. filterStyle.innerHTML =
  72. ".searchable {display: none} .searchable" +
  73. filter_value.split(' ').map(getFilter).join('') +
  74. "{display: block}";
  75. }
  76. handleContentChanged();
  77. }
  78. (function() {
  79. // collect indexes on dt
  80. var nodes = document.getElementsByTagName('dt');
  81. for (var i = 0, l = nodes.length; i < l; i++) {
  82. var el = nodes[i], next = el, index = [];
  83. while ((next = next.nextElementSibling) && !next.id) {
  84. if (next.tagName != 'DIV')
  85. continue;
  86. var childs = next.children;
  87. for (var child of childs) {
  88. index.push(child.getAttribute('data-index'));
  89. }
  90. }
  91. el.setAttribute('data-index', index.join(' '));
  92. };
  93. document.getElementById('filter').addEventListener('input', function() {
  94. filter(this.value);
  95. });
  96. })();
  97. // tag handling
  98. function filterByTag(curTag) {
  99. var games = document.getElementsByTagName('dd');
  100. var game, gameTags;
  101. var parent;
  102. for (var i = 0, len = games.length; i < len; i += 1) {
  103. game = games[i];
  104. gameTags = game.getAttribute('data-tags').split(' ');
  105. parent = document.getElementById(game.getAttribute('data-parent'));
  106. if (gameTags && gameTags.indexOf(curTag) > -1) {
  107. if (!game.classList.contains('active')) {
  108. game.classList.add('active');
  109. parent.classList.add('active');
  110. }
  111. } else if (game.classList.contains('active')) {
  112. game.classList.remove('active');
  113. parent.classList.remove('active');
  114. }
  115. }
  116. handleContentChanged();
  117. }
  118. function sortByUpdated(e) {
  119. const btn = e.target;
  120. var list = document.getElementById('list');
  121. var sorted = document.getElementById('sorted');
  122. if (list.style.display == "none") {
  123. list.style.display = "block";
  124. sorted.style.display = "none";
  125. btn.innerHTML = "Updated";
  126. } else {
  127. list.style.display = "none";
  128. sorted.style.display = "block";
  129. btn.innerHTML = "Originals";
  130. if (!sorted.hasChildNodes()) {
  131. const games = [...document.getElementsByTagName('dd')];
  132. var gameList = [];
  133. let gameNames = new Set();
  134. games.forEach(game => {
  135. if (!gameNames.has(game.dataset.name)) {
  136. gameNames.add(game.dataset.name);
  137. gameList.push(game.cloneNode(true));
  138. }
  139. });
  140. gameList.sort(function(a,b) {
  141. return b.dataset.updated.localeCompare(a.dataset.updated);
  142. });
  143. gameList.forEach(game => {
  144. sorted.appendChild(game);
  145. });
  146. }
  147. }
  148. }
  149. function highlightTags(tag) {
  150. var style = document.getElementById('tag-style');
  151. if (tag) {
  152. tag = tag.replace(/"/g, '');
  153. var line = '.tag[data-name=\"' + tag + '\"] { color: #ccc; background-color: #444; }';
  154. line += '.dark-theme .tag[data-name=\"' + tag + '\"] { color: #444; background-color: #ccc; }';
  155. style.innerHTML = line;
  156. } else {
  157. style.innerHTML = '';
  158. }
  159. }
  160. (function() {
  161. var tags = document.getElementsByClassName('tag');
  162. for (var i = 0, l = tags.length; i < l; i += 1) {
  163. tags[i].addEventListener('click', onclick);
  164. }
  165. function onclick(e) {
  166. var t = e.target.hasAttribute('data-name') ? e.target : e.target.parentNode;
  167. var curTag = t.getAttribute('data-name');
  168. if (curTag === activeTag) {
  169. activeTag = null;
  170. document.body.classList.remove('tags-active');
  171. highlightTags(null);
  172. setQueryParams('tag', null);
  173. } else {
  174. activeTag = curTag;
  175. document.body.classList.add('tags-active');
  176. highlightTags(curTag);
  177. setQueryParams('tag', curTag);
  178. }
  179. filterByTag(curTag);
  180. }
  181. })();
  182. // image validation
  183. (function() {
  184. OSGC.getImages = getImages;
  185. OSGC.downloadImage = downloadImage;
  186. OSGC.validate = validate;
  187. function init() {
  188. OSGC.brokenLinks = {};
  189. OSGC.invalidImages = {
  190. forAnts: [],
  191. tooSmall: [],
  192. tooBig: [],
  193. tooSlow: []
  194. };
  195. }
  196. init();
  197. function getImages() {
  198. let galleries = document.getElementsByClassName('gallery-json');
  199. let galleries_len = galleries.length;
  200. let images = [];
  201. for (let i = 0; i < galleries_len; i += 1) {
  202. let g = galleries[i];
  203. let game = g.getAttribute('data-game');
  204. let cur = g.innerHTML.trim().split(', ').map(
  205. function addGameName(url) {
  206. return { url: url, name: game };
  207. }
  208. );
  209. images = images.concat(cur);
  210. }
  211. console.info('Galleries:', galleries.length);
  212. console.info('Images:', images.length);
  213. return images;
  214. }
  215. function validateImage(image, loadTime) {
  216. let width = image.naturalWidth;
  217. if (width < 200) { OSGC.invalidImages.forAnts.push(image.src); }
  218. else if (width < 400) { OSGC.invalidImages.tooSmall.push(image.src); }
  219. else if (width > 2000) { OSGC.invalidImages.tooBig.push(image.src); }
  220. if (loadTime > 5000) { OSGC.invalidImages.tooSlow.push(image.src); }
  221. }
  222. function downloadImage(dimage) {
  223. return new Promise(function (resolve, reject) {
  224. let timeStart = new Date().getTime();
  225. let image = new Image();
  226. image.onload = onload;
  227. image.onerror = onerror;
  228. image.src = dimage.url;
  229. function time() { return new Date().getTime() - timeStart; }
  230. function onload() { resolve({image: image, time: time()}); }
  231. function onerror() {
  232. reject({
  233. name: dimage.name,
  234. url: dimage.url,
  235. time: time()
  236. });
  237. }
  238. });
  239. }
  240. function reflect(promise){
  241. function resolved(value) { validateImage(value.image, value.time); }
  242. function rejected(err) {
  243. let bad = OSGC.brokenLinks[err.name];
  244. if (!bad) { bad = OSGC.brokenLinks[err.name] = []; }
  245. bad.push(err.url);
  246. }
  247. return promise.then(resolved, rejected);
  248. }
  249. function downloadImages(images) {
  250. return Promise.all(images.map(downloadImage).map(reflect));
  251. }
  252. function showResults() {
  253. console.group('Results');
  254. console.log('Invalid images: %O', OSGC.invalidImages);
  255. console.log('Bad links: %O', OSGC.brokenLinks);
  256. console.groupEnd();
  257. }
  258. function validate(beginAt, turnsMax, concurrentMax) {
  259. let images = getImages();
  260. let images_len = images.length;
  261. let cur = beginAt - 1 || -1;
  262. let turns = cur && turnsMax ? cur + turnsMax : -2;
  263. let max = concurrentMax || 10;
  264. let arrBegin, arrEnd;
  265. init(); // reset
  266. function next() {
  267. cur += 1;
  268. arrBegin = cur * max;
  269. arrEnd = cur * max + max;
  270. console.log('Progress:', arrEnd, '/', images_len);
  271. return downloadImages(images.slice(arrBegin, arrEnd));
  272. }
  273. function queue() {
  274. if (cur === turns || cur >= images_len / max) {
  275. return Promise.resolve();
  276. } else {
  277. return next().then(queue);
  278. }
  279. }
  280. return Promise.resolve().then(queue).then(showResults);
  281. }
  282. })();
  283. // Lazy load badges
  284. (function() {
  285. window.addEventListener('scroll', lazyloadHandler);
  286. window.addEventListener('load', lazyloadHandler);
  287. window.addEventListener('resize', lazyloadHandler);
  288. })();
  289. function getQueryParams() {
  290. var queryParams = window.location.search.substr(1).split('&').reduce(function (q, query) {
  291. var chunks = query.split('=');
  292. var key = chunks[0];
  293. if (typeof(chunks[1]) == 'undefined') {
  294. return ({}, q);
  295. }
  296. var value = decodeURIComponent(chunks[1]);
  297. value = isNaN(Number(value))? value : Number(value);
  298. return (q[key] = value, q);
  299. }, {});
  300. return queryParams;
  301. }
  302. function setQueryParams(key, value) {
  303. if (value === null) {
  304. params = Object.keys(params).reduce(function(object, key_it) {
  305. if (key_it !== key) {
  306. object[key] = params[key];
  307. }
  308. return object;
  309. }, {});
  310. }
  311. else {
  312. params[key] = value;
  313. }
  314. var url = '?';
  315. Object.keys(params).map(function(key, i) {
  316. if (i !== 0) {
  317. url += '&';
  318. }
  319. url += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
  320. });
  321. history.replaceState({}, null, url);
  322. }
  323. function setCount() {
  324. var list = document.getElementById('list');
  325. var sorted = document.getElementById('sorted');
  326. if (list.style.display == "none") {
  327. var dds = Array.from(sorted.getElementsByTagName('dd'));
  328. var total = dds.length;
  329. } else {
  330. var dds = Array.from(list.getElementsByTagName('dd'));
  331. var total = new Set(dds.map(dd => dd.getAttribute('data-name'))).size;
  332. }
  333. var shownGames = dds.filter(dd => dd.getBoundingClientRect().width + dd.getBoundingClientRect().height > 0)
  334. var shown = new Set(shownGames.map(dd => dd.getAttribute('data-name'))).size;
  335. document.getElementsByClassName('nav-count')[0].innerHTML = shown + "/" + total + " games";
  336. }
  337. (function () {
  338. setCount();
  339. params = getQueryParams();
  340. if (params.hasOwnProperty('tag')) {
  341. activeTag = params['tag'];
  342. document.body.classList.add('tags-active');
  343. highlightTags(params['tag']);
  344. setQueryParams('tag', params['tag']);
  345. filterByTag(params['tag']);
  346. }
  347. if (params.hasOwnProperty('filter')) {
  348. filter(params['filter']);
  349. document.getElementById('filter').value = params['filter'];
  350. }
  351. const sortBtnLbl = document.getElementById('sort-button-label');
  352. sortBtnLbl.innerHTML = "Updated";
  353. sortBtnLbl.addEventListener('click', sortByUpdated);
  354. })();