index.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  1. "use strict";
  2. var settings = {
  3. baseURL: "<!--%#INSERT:DOCSURL#%-->",
  4. // baseURL: "http://127.0.0.1:8076/", // DEBUG
  5. jokeEndpoint: "joke",
  6. anyCategoryName: "Any",
  7. defaultFormat: "json",
  8. submitUrl: "<!--%#INSERT:DOCSURL#%-->/submit",
  9. // submitUrl: "http://127.0.0.1:8076/submit", // DEBUG
  10. defaultLang: "en",
  11. formatVersion: 3,
  12. contributorsObject: JSON.parse('<!--%#INSERT:CONTRIBUTORS#%-->'),
  13. categoryAliasesObject: JSON.parse('<!--%#INSERT:CATEGORYALIASES#%-->')
  14. };
  15. var submission = {};
  16. if(settings.baseURL.endsWith("/"))
  17. {
  18. settings.baseURL = settings.baseURL.substr(0, (settings.baseURL.length - 1));
  19. }
  20. var tryItOk = false;
  21. var tryItURL = "";
  22. var dIHTML = `
  23. <h2>To provide this service to you, JokeAPI needs to collect some anonymous data.</h2>
  24. <br>
  25. <a href="<!--%#INSERT:PRIVACYPOLICYURL#%-->" target="_blank">View the privacy policy by clicking here.</a>
  26. <br><br><br>
  27. <b>This is a list of everything JokeAPI stores temporarily (this data will be deleted after about a month):</b><br>
  28. <ul>
  29. <li>A hash of your IP address, your request headers, the request method and the request date and time (this will be kept inside a secure log file so I can debug JokeAPI and help you solve any issues you might have)</li>
  30. </ul>
  31. <br><br>
  32. <b>This is a list of everything JokeAPI stores indefinitely:</b><br>
  33. <ul>
  34. <li>A hash of your IP address <u>if it gets added to the <i>blacklist</i>.</u> This happens if you have shown malicious behavior or have exceeded the rate limiting for too long / often</li>
  35. <li>A hash of your IP address <u>if it gets added to a <i>whitelist</i>.</u> This only happens if you contacted me to get more requests per minute or are partnered with me and have been informed that this is happening</li>
  36. <li>A hash of your IP address <u>if it gets added to a <i>console blacklist</i>.</u> This (if at all) also only happens if you are partnered with me</li>
  37. <li>The requested URL, consisting of the URL path, the URL parameters and the URL anchor</li>
  38. <li>The payload of joke submissions (using POST requests on the submission endpoint)</li>
  39. </ul>
  40. <br><br>
  41. <b>Terminology:</b><br>
  42. &nbsp;&nbsp;&nbsp;&nbsp;Hash:<br>
  43. &nbsp;&nbsp;&nbsp;&nbsp;A hash uses an algorithm to encrypt the input to something that cannot be reconstructed to the initial input again.<br>
  44. &nbsp;&nbsp;&nbsp;&nbsp;In the case of JokeAPI, your IP address gets hashed and stored to a database.<br>
  45. &nbsp;&nbsp;&nbsp;&nbsp;In this hashed state, your original IP address can not be reconstructed and you will stay completely anonymous in case any data ever gets leaked, while JokeAPI can still uniquely identify you.
  46. <br><br><br>
  47. Please note that the collection of the above listed data is necessary to provide you this service.<br>
  48. Without it, rate limiting wouldn't be possible. This would lead to the API and all of my other services being taken down by DoS-attacks.<br>
  49. This has already happened before and it has impacted all of my services to the point of them being completely unresponsive.<br><br>
  50. You can request to get your collected data deleted or to view the data about you that JokeAPI collected (according to <a href="https://www.privacy-regulation.eu/en/article-12-transparent-information-communication-and-modalities-for-the-exercise-of-the-rights-of-the-data-subject-GDPR.htm">article 12 GDPR</a>) by sending me an e-mail: <a href="mailto:[email protected]">[email protected]</a>
  51. <br><br><br>
  52. `;
  53. var rsIHTML = `
  54. <form onsubmit="submitRestartForm()">
  55. <input type="password" id="restartFormToken" placeholder="Restart Token" style="width:30vw"> <button type="submit">Send &gt;</button>
  56. </form>
  57. `;
  58. var sMenu=new function(){this.new=function(id,title,innerhtml,width,height,border_rounded,closable_ESC,closable_btn,on_close,close_img_src){if(typeof id=="string"&&typeof title=="string"&&typeof innerhtml=="string"&&typeof width=="number"&&typeof height=="number"){if(gebid("jsg_menu_"+id)!=null){console.error("a menu with the ID "+id+" already exists - not creating a new one");return}
  59. /* eslint-disable-next-line */
  60. if(!border_rounded)border_rounded=!0;if(typeof closable_ESC!="boolean")closable_ESC=!0;if(typeof closable_btn!="boolean")closable_btn=!0;if(!on_close)on_close=function(){};if(!close_img_src)close_img_src="https://sv443.net/resources/images/jsg_menu_close.png";var menuelem=document.createElement("div");menuelem.style.display="none";menuelem.style.opacity="0";menuelem.style.transition="opacity 0.3s ease-in";menuelem.style.overflow="auto";menuelem.className="jsg_menu";menuelem.id="jsg_menu_"+id;menuelem.style.position="fixed";menuelem.style.top=((100-height)/2)+"vh";menuelem.style.left=((100-width)/2)+"vw";menuelem.style.width=width+"vw";menuelem.style.height=height+"vh";menuelem.style.padding="10px";menuelem.style.border="2px solid #454545";if(border_rounded)menuelem.style.borderRadius="1.2em";else menuelem.style.borderRadius="0";if(closable_btn)var closebtnih='<img onclick="sMenu.close(\''+id+'\')" class="jsg_menuclosebtn" title="Close" src="https://sv443.net/cdn/jsl/closebtn.png" style="cursor:pointer;position:absolute;top:0;right:0;width:1.5em;height:1.5em;">';else closebtnih="";menuelem.style.backgroundColor="#ddd";menuelem.innerHTML="<div class='jsg_menutitle' style='font-size:1.5em;text-align:center;'>"+title+"</div>"+closebtnih+"<br>"+innerhtml;document.body.appendChild(menuelem);if(closable_ESC)document.addEventListener("keydown",function(e){if(e.keyCode==27)sMenu.close(id)})}
  61. else{console.error("the arguments for Menu.new() are wrong");return!1}}
  62. this.close=function(id){try{setTimeout(function(){gebid("jsg_menu_"+id).style.display="none"},500);gebid("jsg_menu_"+id).style.opacity="0";gebid("jsg_menu_"+id).style.transition="opacity 0.3s ease-in"}
  63. catch(err){console.error("couldn't find menu with id "+id+". Is the ID correct and was the menu created correctly?");return!1}}
  64. this.open=function(id){try{gebid("jsg_menu_"+id).style.display="block";setTimeout(function(){gebid("jsg_menu_"+id).style.opacity="1";gebid("jsg_menu_"+id).style.transition="opacity 0.3s ease-out"},20)}
  65. catch(err){console.error("couldn't find menu with id "+id+". Is the ID correct and was the menu created correctly?");return!1}}
  66. this.theme=function(id,theme){try{if(theme=="dark"){gebid("jsg_menu_"+id).style.backgroundColor="#454545";gebid("jsg_menu_"+id).style.color="white";gebid("jsg_menu_"+id).style.borderColor="#ddd";gebid("jsg_menu_"+id).style.transition="background-color 0.4s ease-out, color 0.4s ease-out, border-color 0.4s ease-out"}
  67. else{gebid("jsg_menu_"+id).style.backgroundColor="#ddd";gebid("jsg_menu_"+id).style.color="black";gebid("jsg_menu_"+id).style.borderColor="#454545";gebid("jsg_menu_"+id).style.transition="background-color 0.4s ease-out, color 0.4s ease-out, border-color 0.4s ease-out"}}
  68. catch(err){console.error("couldn't find menu with id "+id+". Is the ID correct and was the menu created correctly?");return!1}}
  69. this.setInnerHTML=function(id,inner_html){try{gebid("jsg_menu_"+id).innerHTML=inner_html}
  70. catch(err){console.error("couldn't find menu or inner_html is not valid");return!1}}
  71. this.setOuterHTML=function(id,outer_html){try{gebid("jsg_menu_"+id).outerHTML=outer_html}
  72. catch(err){console.error("couldn't find menu or outer_html is not valid");return!1}}}
  73. /**
  74. * Alias for document.getElementById()
  75. * @param {String} id ID of the element
  76. */
  77. function gebid(id){return document.getElementById(id);}
  78. var idRanges = {};
  79. var maxJokeIdRange = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  80. //#MARKER onload
  81. function onLoad()
  82. {
  83. window.jokeapi = {};
  84. console.log("%cJokeAPI%cDocumentation (v<!--%#INSERT:VERSION#%-->) - © Copyright Sv443 Network 2018-2020", "color: #b05ffc; background-color: black; padding: 5px; padding-right: 0; font-size: 1.2em;", "color: white; background-color: black; padding: 5px; font-size: 1.2em;");
  85. gebid("content").onclick = function(e){tryCloseSideNav(e)};
  86. document.getElementsByTagName("header")[0].onclick = function(e){tryCloseSideNav(e)};
  87. gebid("docTitle").onclick = function(){window.location.reload()};
  88. addCodeTabs();
  89. sMenu.new("privacyPolicy", "What data does JokeAPI collect?", dIHTML, 85, 85, true, true, true);
  90. sMenu.theme("privacyPolicy", "dark");
  91. sMenu.new("restartPrompt", "Restart <!--%#INSERT:NAME#%-->", rsIHTML, 40, 30, true, true, true);
  92. sMenu.theme("restartPrompt", "dark");
  93. // eslint-disable-next-line no-undef
  94. if(Cookies.get("hideUsageTerms") == "true")
  95. {
  96. gebid("usageTerms").style.display = "none";
  97. }
  98. else
  99. {
  100. gebid("usageTerms").style.display = "inline-block";
  101. }
  102. document.addEventListener("keydown", function(e) {
  103. if(e.key == "Escape" && window.jokeapi.sidenavOpened)
  104. closeNav();
  105. else if(e.key == "R" && e.altKey && e.shiftKey)
  106. {
  107. openRestartForm();
  108. }
  109. });
  110. resetTryItForm();
  111. setTimeout(function() {
  112. gebid("usageTerms").dataset.animateBorder = "true";
  113. }, 800);
  114. buildURL();
  115. gebid("content").addEventListener("click", function(e) {
  116. if(document.body.dataset["sidenav"] == "opened")
  117. {
  118. e.preventDefault();
  119. closeNav();
  120. }
  121. });
  122. var selectOnInitElems = document.getElementsByClassName("selectOnInit");
  123. for(var i = 0; i < selectOnInitElems.length; i++)
  124. {
  125. selectOnInitElems[i].selected = true;
  126. }
  127. var uncheckOnInitElems = document.getElementsByClassName("uncheckOnInit");
  128. for(var iii = 0; iii < uncheckOnInitElems.length; iii++)
  129. {
  130. uncheckOnInitElems[iii].checked = false;
  131. }
  132. var clearOnInitElems = document.getElementsByClassName("clearOnInit");
  133. for(var j = 0; j < clearOnInitElems.length; j++)
  134. {
  135. clearOnInitElems[j].value = "";
  136. }
  137. // insert dates
  138. document.querySelectorAll(".insert-current-year").forEach((e) => e.innerText = new Date().getFullYear());
  139. try
  140. {
  141. var fileFormats = JSON.parse('<!--%#INSERT:FILEFORMATARRAY#%-->');
  142. if(fileFormats.includes("JSON"))
  143. {
  144. fileFormats.splice(fileFormats.indexOf("JSON"), 1);
  145. }
  146. Array.from(document.getElementsByClassName("insFormatsS")).forEach(function(el) {
  147. el.innerText = fileFormats.join(" and ");
  148. });
  149. var flags = JSON.parse('<!--%#INSERT:FLAGSARRAY#%-->');
  150. Array.from(document.getElementsByClassName("insFlags")).forEach(function(el) {
  151. el.innerText = flags.join(", ");
  152. });
  153. var formats = JSON.parse('<!--%#INSERT:FILEFORMATARRAY#%-->');
  154. Array.from(document.getElementsByClassName("insFormats")).forEach(function(el) {
  155. el.innerText = formats.join(", ").toLowerCase();
  156. });
  157. var categories = JSON.parse('<!--%#INSERT:CATEGORYARRAY#%-->');
  158. Array.from(document.getElementsByClassName("insCategories")).forEach(function(el) {
  159. el.innerText = categories.join(", ");
  160. });
  161. }
  162. catch(err)
  163. {
  164. return alert("Documentation compilation was unsuccessful: Value insertion error:\n" + err);
  165. }
  166. //#SECTION submission form
  167. var inputElems = [
  168. "f_category",
  169. "f_type",
  170. "f_flags_nsfw",
  171. "f_flags_religious",
  172. "f_flags_political",
  173. "f_flags_racist",
  174. "f_flags_sexist",
  175. "f_flags_explicit",
  176. "f_setup",
  177. "f_delivery",
  178. "f_language"
  179. ];
  180. for(var ii = 0; ii < inputElems.length; ii++)
  181. {
  182. var elm = gebid(inputElems[ii]);
  183. if(elm.tagName.toLowerCase() != "textarea")
  184. {
  185. elm.onchange = function()
  186. {
  187. return valChanged(this);
  188. }
  189. }
  190. else
  191. {
  192. elm.oninput = function()
  193. {
  194. return valChanged(this);
  195. }
  196. }
  197. }
  198. var langXhr = new XMLHttpRequest();
  199. var langUrl = settings.baseURL + "/languages";
  200. var langSelectElems = document.getElementsByClassName("appendLangOpts");
  201. var otherOpt = document.createElement("option");
  202. otherOpt.innerText = "Other / Custom";
  203. otherOpt.value = "other";
  204. gebid("f_language").appendChild(otherOpt);
  205. var sysLangsText = "";
  206. var jokeLangsText = "";
  207. langXhr.open("GET", langUrl);
  208. langXhr.onreadystatechange = function() {
  209. var xErrElem;
  210. if(langXhr.readyState == 4 && langXhr.status < 300)
  211. {
  212. var respJSON = JSON.parse(langXhr.responseText.toString());
  213. var languagesArray = respJSON.jokeLanguages;
  214. sysLangsText = respJSON.systemLanguages.join(", ");
  215. jokeLangsText = respJSON.jokeLanguages.join(", ");
  216. for(var i = 0; i < languagesArray.length; i++)
  217. {
  218. for(var j = 0; j < langSelectElems.length; j++)
  219. {
  220. var langName = "";
  221. for(var k = 0; k < respJSON.possibleLanguages.length; k++)
  222. {
  223. if(respJSON.possibleLanguages[k].code == languagesArray[i])
  224. {
  225. langName = respJSON.possibleLanguages[k].name;
  226. }
  227. }
  228. xErrElem = document.createElement("option");
  229. xErrElem.value = languagesArray[i];
  230. if(languagesArray[i] == settings.defaultLang)
  231. {
  232. xErrElem.selected = true;
  233. }
  234. xErrElem.innerText = languagesArray[i] + " - " + langName;
  235. if(languagesArray[i] == settings.defaultLang)
  236. xErrElem.selected = true;
  237. langSelectElems[j].appendChild(xErrElem);
  238. langSelectElems[j].value = settings.defaultLang;
  239. }
  240. }
  241. var sysLangsElems = document.getElementsByClassName("insSysLangs");
  242. var jokeLangsElems = document.getElementsByClassName("insJokeLangs");
  243. for(var sI = 0; sI < sysLangsElems.length; sI++)
  244. {
  245. sysLangsElems[sI].innerText = sysLangsText;
  246. }
  247. for(var jI = 0; jI < sysLangsElems.length; jI++)
  248. {
  249. jokeLangsElems[jI].innerText = jokeLangsText;
  250. }
  251. }
  252. else if(langXhr.readyState == 4 && langXhr.status >= 300)
  253. {
  254. for(var ii = 0; ii < langSelectElems.length; ii++)
  255. {
  256. xErrElem = document.createElement("option");
  257. xErrElem.value = "en";
  258. xErrElem.innerText = "en - English";
  259. xErrElem.selected = true;
  260. langSelectElems[ii].appendChild(xErrElem);
  261. }
  262. }
  263. };
  264. langXhr.send();
  265. var infoXhr = new XMLHttpRequest();
  266. infoXhr.open("GET", (settings.baseURL + "/info"));
  267. infoXhr.onreadystatechange = function() {
  268. if(infoXhr.readyState == 4 && infoXhr.status < 300)
  269. {
  270. var respJSON = JSON.parse(infoXhr.responseText.toString());
  271. var idrKeys = Object.keys(respJSON.jokes.idRange);
  272. for(var i = 0; i < idrKeys.length; i++)
  273. {
  274. var idrKey = idrKeys[i];
  275. idRanges[idrKey] = respJSON.jokes.idRange[idrKey];
  276. try
  277. {
  278. console.info("<!--%#INSERT:NAME#%--> is serving " + (respJSON.jokes.idRange[idrKey][1] + 1) + " jokes from language \"" + idrKey + "\"");
  279. }
  280. catch(err)
  281. {
  282. void(err);
  283. }
  284. }
  285. reRender();
  286. }
  287. else if(infoXhr.readyState == 4 && infoXhr.status >= 300)
  288. {
  289. console.error("Couldn't get ID range of all languages. Defaulting to the max possible value.");
  290. }
  291. };
  292. infoXhr.send();
  293. gebid("submitBtn").addEventListener("click", function() {
  294. submitJoke();
  295. });
  296. buildSubmission();
  297. setTimeout(function() { buildSubmission() }, 2000);
  298. gebid("insUserAgent").innerText = navigator.userAgent;
  299. gebid("lcodeSelect").value = settings.defaultLang;
  300. var abElems = document.getElementsByClassName("antiBotE");
  301. for(var l = 0; l < abElems.length; l++)
  302. {
  303. abElems[l].onclick = function()
  304. {
  305. if(!this.classList.contains("shown"))
  306. {
  307. this.innerText = atob(this.dataset.enc);
  308. this.classList.add("shown");
  309. }
  310. }
  311. }
  312. gebid("lcodeSelect").value = settings.defaultLang;
  313. gebid("lcodeSelect").onchange = function() { reRender(true) };
  314. gebid("sideNavOpen").onclick = function() { return openNav(); };
  315. loadCategoryAliases();
  316. loadContributors();
  317. }
  318. /**
  319. * @param {MouseEvent} e
  320. */
  321. function tryCloseSideNav(e)
  322. {
  323. if(document.body.dataset["sidenav"] == "opened")
  324. {
  325. e.preventDefault();
  326. closeNav();
  327. }
  328. }
  329. function addCodeTabs()
  330. {
  331. var codeElements = document.getElementsByTagName("code");
  332. for(var i = 0; i < codeElements; i++)
  333. {
  334. if(codeElements[i].classList.contains("prettyprint"))
  335. codeElements[i].innerHTML = codeElements[i].innerHTML.replace(/&tab;/gm, "&nbsp;&nbsp;&nbsp;&nbsp;");
  336. }
  337. }
  338. //#MARKER SideNav
  339. function openNav()
  340. {
  341. console.info("opening nav");
  342. setTimeout(function() {
  343. document.body.dataset["sidenav"] = "opened";
  344. }, 50);
  345. window.jokeapi.sidenavOpened = true;
  346. gebid("sidenav").style.width = "280px";
  347. gebid("content").style.marginLeft = "280px";
  348. document.getElementsByTagName("header")[0].dataset["grayscaled"] = "true";
  349. gebid("sideNavOpen").style.visibility = "hidden";
  350. }
  351. function closeNav()
  352. {
  353. console.info("closing nav");
  354. if(document.body.dataset["sidenav"] != "opened")
  355. return;
  356. window.jokeapi.sidenavOpened = false;
  357. document.body.dataset["sidenav"] = "closed";
  358. gebid("sidenav").style.width = "0";
  359. gebid("content").style.marginLeft = "10px";
  360. document.getElementsByTagName("header")[0].dataset["grayscaled"] = "false";
  361. gebid("sideNavOpen").style.visibility = "visible";
  362. }
  363. // function getQueryStringObject()
  364. // {
  365. // var qstrObj = {};
  366. // if(!window.location.href.includes("?"))
  367. // return null;
  368. // var rawQstr = window.location.href.split("?")[1];
  369. // var qstrArr = [];
  370. // if(rawQstr.includes("#"))
  371. // rawQstr = rawQstr.split("#")[0];
  372. // if(rawQstr != null && rawQstr.includes("&"))
  373. // qstrArr = rawQstr.split("&");
  374. // else if(rawQstr != null)
  375. // qstrArr = [rawQstr];
  376. // else return null;
  377. // if(qstrArr.length > 0)
  378. // qstrArr.forEach(function(qstrEntry) {
  379. // if(qstrEntry.includes("="))
  380. // qstrObj[qstrEntry.split("=")[0]] = qstrEntry.split("=")[1];
  381. // });
  382. // else return null;
  383. // return qstrObj;
  384. // }
  385. /**
  386. * @param {Boolean} [langChanged]
  387. */
  388. function reRender(langChanged)
  389. {
  390. console.info("Re-rendering try-it form");
  391. var allOk = true;
  392. //#SECTION category
  393. var isValid = false;
  394. document.getElementsByName("catSelect").forEach(function(el) {
  395. if(!el.checked)
  396. return;
  397. if(el.value == "any")
  398. {
  399. isValid = true;
  400. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  401. gebid(cat).disabled = true;
  402. });
  403. }
  404. else
  405. {
  406. var isChecked = false;
  407. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  408. var cel = gebid(cat);
  409. cel.disabled = false;
  410. if(cel.checked)
  411. isChecked = true;
  412. });
  413. if(isChecked)
  414. isValid = true;
  415. }
  416. });
  417. if(!isValid)
  418. {
  419. allOk = false;
  420. gebid("categoryWrapper").style.borderColor = "red";
  421. }
  422. else
  423. {
  424. gebid("categoryWrapper").style.borderColor = "initial";
  425. }
  426. //#SECTION format
  427. if(!gebid("typ-cb1").checked && !gebid("typ-cb2").checked)
  428. {
  429. allOk = false;
  430. gebid("typeSelectWrapper").style.borderColor = "red";
  431. }
  432. else
  433. {
  434. gebid("typeSelectWrapper").style.borderColor = "initial";
  435. }
  436. //#SECTION id range
  437. if(langChanged === true)
  438. {
  439. console.warn("langchanged")
  440. var langCode = gebid("lcodeSelect").value;
  441. if(idRanges[langCode])
  442. {
  443. var maxRange = parseInt(idRanges[langCode][1]);
  444. gebid("idRangeInputTo").max = maxRange;
  445. gebid("idRangeInputTo").value = maxRange;
  446. maxJokeIdRange = maxRange;
  447. }
  448. else
  449. {
  450. gebid("idRangeInputTo").max = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  451. maxJokeIdRange = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  452. }
  453. }
  454. var numRegex = /^[0-9]+$/gm;
  455. var fromVal = gebid("idRangeInputFrom").value;
  456. var toVal = gebid("idRangeInputTo").value;
  457. var fromValInt = parseInt(fromVal);
  458. var toValInt = parseInt(toVal);
  459. var outOfRange = (fromValInt < 0 || toValInt > maxJokeIdRange);
  460. var notNumber = ((fromVal.match(numRegex) == null) || (toVal.match(numRegex) == null));
  461. if(outOfRange || notNumber || fromValInt > toValInt)
  462. {
  463. allOk = false;
  464. gebid("idRangeWrapper").style.borderColor = "red";
  465. }
  466. else
  467. {
  468. gebid("idRangeWrapper").style.borderColor = "initial";
  469. }
  470. var jokesAmount = parseInt(gebid("jokesAmountInput").value);
  471. if(jokesAmount > parseInt("<!--%#INSERT:MAXJOKEAMOUNT#%-->") || jokesAmount < 1 || isNaN(jokesAmount))
  472. {
  473. allOk = false;
  474. gebid("jokeAmountWrapper").style.borderColor = "red";
  475. }
  476. else
  477. {
  478. gebid("jokeAmountWrapper").style.borderColor = "initial";
  479. }
  480. if(allOk)
  481. {
  482. tryItOk = true;
  483. }
  484. else
  485. {
  486. tryItOk = false;
  487. }
  488. buildURL();
  489. }
  490. //#MARKER build URL
  491. function buildURL()
  492. {
  493. var queryParams = [];
  494. //#SECTION categories
  495. var selectedCategories = [settings.anyCategoryName];
  496. if(gebid("cat-radio2").checked)
  497. {
  498. selectedCategories = [];
  499. if(gebid("cat-cb1").checked)
  500. {
  501. selectedCategories.push("Programming");
  502. }
  503. if(gebid("cat-cb2").checked)
  504. {
  505. selectedCategories.push("Miscellaneous");
  506. }
  507. if(gebid("cat-cb3").checked)
  508. {
  509. selectedCategories.push("Dark");
  510. }
  511. if(gebid("cat-cb4").checked)
  512. {
  513. selectedCategories.push("Pun");
  514. }
  515. if(gebid("cat-cb5").checked)
  516. {
  517. selectedCategories.push("Spooky");
  518. }
  519. if(gebid("cat-cb6").checked)
  520. {
  521. selectedCategories.push("Christmas");
  522. }
  523. if(selectedCategories.length == 0)
  524. {
  525. selectedCategories.push(settings.anyCategoryName);
  526. }
  527. }
  528. //#SECTION language
  529. var langCode = gebid("lcodeSelect").value || settings.defaultLang;
  530. if(langCode != settings.defaultLang)
  531. queryParams.push("lang=" + langCode);
  532. //#SECTION flags
  533. var flagElems = [gebid("blf-cb1"), gebid("blf-cb2"), gebid("blf-cb3"), gebid("blf-cb4"), gebid("blf-cb5"), gebid("blf-cb6")];
  534. var flagNames = JSON.parse('<!--%#INSERT:FLAGSARRAY#%-->');
  535. var selectedFlags = [];
  536. flagElems.forEach(function(el, i) {
  537. if(el.checked)
  538. {
  539. selectedFlags.push(flagNames[i]);
  540. }
  541. });
  542. if(selectedFlags.length > 0)
  543. {
  544. queryParams.push("blacklistFlags=" + selectedFlags.join(","));
  545. }
  546. //#SECTION format
  547. var formatElems = [gebid("fmt-cb1"), gebid("fmt-cb2"), gebid("fmt-cb3"), gebid("fmt-cb4")];
  548. formatElems.forEach(function(el) {
  549. if(el.checked && el.value != settings.defaultFormat)
  550. {
  551. queryParams.push("format=" + el.value);
  552. }
  553. });
  554. //#SECTION type
  555. var singleJoke = gebid("typ-cb1").checked;
  556. var twopartJoke = gebid("typ-cb2").checked;
  557. if(singleJoke ^ twopartJoke == 1)
  558. {
  559. if(singleJoke)
  560. {
  561. queryParams.push("type=single");
  562. }
  563. else if(twopartJoke)
  564. {
  565. queryParams.push("type=twopart");
  566. }
  567. }
  568. //#SECTION search string
  569. var sstr = gebid("searchStringInput").value;
  570. if(sstr)
  571. {
  572. queryParams.push("contains=" + encodeURIComponent(sstr));
  573. }
  574. //#SECTION id range
  575. var range = [parseInt(gebid("idRangeInputFrom").value), parseInt(gebid("idRangeInputTo").value)];
  576. if(!isNaN(range[0]) && !isNaN(range[1]) && range[0] >= 0 && range[1] <= maxJokeIdRange && range[1] >= range[0])
  577. {
  578. if(range[0] == range[1] && range[0] >= 0 && range[0] <= maxJokeIdRange)
  579. {
  580. // Use "x" format
  581. queryParams.push("idRange=" + range[0]);
  582. }
  583. else if(range[0] != 0 || range[1] != maxJokeIdRange)
  584. {
  585. // Use "x-y" format
  586. queryParams.push("idRange=" + range[0] + "-" + range[1]);
  587. }
  588. }
  589. //#SECTION amount
  590. var jokeAmount = parseInt(gebid("jokesAmountInput").value);
  591. if(jokeAmount != 1 && !isNaN(jokeAmount) && jokeAmount > 0 && jokeAmount <= parseInt("<!--%#INSERT:MAXJOKEAMOUNT#%-->"))
  592. {
  593. queryParams.push("amount=" + jokeAmount);
  594. }
  595. tryItURL = settings.baseURL + "/" + settings.jokeEndpoint + "/" + selectedCategories.join(",");
  596. if(queryParams.length > 0)
  597. {
  598. tryItURL += "?" + queryParams.join("&");
  599. }
  600. gebid("urlBuilderUrl").innerText = tryItURL;
  601. }
  602. //#MARKER send request
  603. function sendTryItRequest()
  604. {
  605. var sendStartTimestamp = new Date().getTime();
  606. var prpr = gebid("urlBuilderPrettyprint");
  607. var tryItRequestError = function(err) {
  608. if(prpr.classList.contains("prettyprint"))
  609. {
  610. prpr.classList.remove("prettyprint");
  611. }
  612. if(prpr.classList.contains("prettyprinted"))
  613. {
  614. prpr.classList.remove("prettyprinted");
  615. }
  616. gebid("tryItResult").innerHTML = "Error:<br><br>" + err;
  617. }
  618. if(!tryItOk)
  619. {
  620. return tryItRequestError("One or more of the parameters you specified are invalid.\nThey are outlined with a red border.\n\nPlease correct the parameters and try again.");
  621. }
  622. if(!prpr.classList.contains("prettyprint"))
  623. {
  624. prpr.classList.add("prettyprint");
  625. }
  626. if(prpr.classList.contains("prettyprinted"))
  627. {
  628. prpr.classList.remove("prettyprinted");
  629. }
  630. if(prpr.classList.contains("lang-json"))
  631. {
  632. prpr.classList.remove("lang-json");
  633. }
  634. if(prpr.classList.contains("lang-yaml"))
  635. {
  636. prpr.classList.remove("lang-yaml");
  637. }
  638. if(prpr.classList.contains("lang-xml"))
  639. {
  640. prpr.classList.remove("lang-xml");
  641. }
  642. prpr.classList.add("lang-json");
  643. try
  644. {
  645. var xhr = new XMLHttpRequest();
  646. xhr.open("GET", tryItURL);
  647. xhr.onreadystatechange = function() {
  648. if(xhr.readyState == 4 && (xhr.status < 300 || xhr.status == 429))
  649. {
  650. var result = "";
  651. if(xhr.getResponseHeader("content-type").includes("json"))
  652. {
  653. result = JSON.stringify(JSON.parse(xhr.responseText.toString()), null, 4);
  654. }
  655. else
  656. {
  657. if(xhr.getResponseHeader("content-type").includes("xml"))
  658. {
  659. gebid("urlBuilderPrettyprint").classList.remove("lang-json");
  660. gebid("urlBuilderPrettyprint").classList.add("lang-xml");
  661. }
  662. else
  663. {
  664. gebid("urlBuilderPrettyprint").classList.remove("lang-json");
  665. gebid("urlBuilderPrettyprint").classList.add("lang-yaml");
  666. }
  667. result = xhr.responseText.toString();
  668. result = result.replace(/[<]/gm, "&lt;");
  669. result = result.replace(/[>]/gm, "&gt;");
  670. }
  671. gebid("tryItResult").innerText = result;
  672. gebid("tryItFormLatency").innerText = "Latency: " + (new Date().getTime() - sendStartTimestamp) + " ms";
  673. PR.prettyPrint(); // eslint-disable-line no-undef
  674. }
  675. else
  676. {
  677. tryItRequestError(xhr.responseText);
  678. }
  679. }
  680. xhr.send();
  681. }
  682. catch(err)
  683. {
  684. tryItRequestError(err);
  685. }
  686. }
  687. //#MARKER interactive elements
  688. function resetTryItForm(confirmation)
  689. {
  690. if(confirmation === true && !confirm("Do you really want to reset the form?"))
  691. return;
  692. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  693. gebid(cat).checked = false;
  694. });
  695. gebid("cat-radio1").checked = true;
  696. ["blf-cb1", "blf-cb2", "blf-cb3", "blf-cb4", "blf-cb5", "blf-cb6"].forEach(function(flg) {
  697. gebid(flg).checked = false;
  698. });
  699. gebid("fmt-cb1").checked = true;
  700. ["typ-cb1", "typ-cb2"].forEach(function(type) {
  701. gebid(type).checked = true;
  702. });
  703. gebid("searchStringInput").value = "";
  704. gebid("idRangeInputFrom").value = 0;
  705. gebid("idRangeInputTo").value = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  706. gebid("jokesAmountInput").value = 1;
  707. reRender();
  708. }
  709. //#MARKER submit joke
  710. function submitJoke()
  711. {
  712. var submitBtn = gebid("submitBtn");
  713. if(submitBtn.disabled == true)
  714. {
  715. return;
  716. }
  717. var xhr = new XMLHttpRequest();
  718. xhr.open("POST", settings.submitUrl);
  719. xhr.onreadystatechange = function() {
  720. if(xhr.readyState == 4)
  721. {
  722. var res;
  723. try
  724. {
  725. res = JSON.parse(xhr.responseText);
  726. }
  727. catch(err)
  728. {
  729. alert("Error " + res.status + " while sending your submission:\n" + res.message + (res.additionalInfo ? "\n\nAdditional info:\n" + res.additionalInfo : ""));
  730. return;
  731. }
  732. if(xhr.status < 300)
  733. {
  734. if(res.error == false)
  735. {
  736. submitBtn.disabled = true;
  737. alert(res.message);
  738. setTimeout(function() {
  739. gebid("submitBtn").disabled = false;
  740. }, 2000);
  741. }
  742. else if(res.error == true)
  743. {
  744. alert("Error " + res.status + " while sending your submission:\n" + res.message + (res.additionalInfo ? "\n\nAdditional info:\n" + res.additionalInfo : ""));
  745. }
  746. }
  747. else if(xhr.status >= 300)
  748. {
  749. var addInfo = res.message;
  750. if(res.additionalInfo)
  751. {
  752. addInfo = res.additionalInfo;
  753. }
  754. alert("Error while sending your submission:\n" + addInfo);
  755. }
  756. }
  757. };
  758. xhr.send(JSON.stringify(submission, null, 4));
  759. }
  760. function valChanged(element)
  761. {
  762. if(element.id == "f_type")
  763. {
  764. if(element.value == "single")
  765. {
  766. gebid("f_setup").placeholder = "Joke";
  767. gebid("f_delivery").style.display = "none";
  768. }
  769. else if(element.value == "twopart")
  770. {
  771. gebid("f_setup").placeholder = "Setup";
  772. gebid("f_delivery").style.display = "initial";
  773. }
  774. }
  775. if(element.id == "f_language")
  776. {
  777. if(element.value == "other")
  778. {
  779. gebid("f_langHideContainer").classList.remove("hidden");
  780. }
  781. else
  782. {
  783. gebid("f_langHideContainer").classList.add("hidden");
  784. }
  785. }
  786. buildSubmission();
  787. }
  788. function buildSubmission()
  789. {
  790. var category = gebid("f_category").value;
  791. var type = gebid("f_type").value;
  792. submission = {
  793. formatVersion: settings.formatVersion,
  794. category: category,
  795. type: type
  796. }
  797. if(type == "single")
  798. {
  799. submission.joke = gebid("f_setup").value;
  800. }
  801. else if(type == "twopart")
  802. {
  803. submission.setup = gebid("f_setup").value;
  804. submission.delivery = gebid("f_delivery").value;
  805. }
  806. var sLang = gebid("f_language").value || settings.defaultLang;
  807. if(sLang == "other")
  808. {
  809. var elVal = gebid("f_customLang").value;
  810. if(elVal && elVal.length == 2)
  811. {
  812. sLang = elVal;
  813. }
  814. else
  815. {
  816. sLang = "Please enter 2 char language code";
  817. }
  818. }
  819. submission = {
  820. ...submission,
  821. flags: {
  822. nsfw: gebid("f_flags_nsfw").checked,
  823. religious: gebid("f_flags_religious").checked,
  824. political: gebid("f_flags_political").checked,
  825. racist: gebid("f_flags_racist").checked,
  826. sexist: gebid("f_flags_sexist").checked,
  827. explicit: gebid("f_flags_explicit").checked
  828. },
  829. lang: sLang
  830. };
  831. var subDisp = gebid("submissionDisplay");
  832. var escapedSubmission = JSON.parse(JSON.stringify(submission)); // copy value without reference
  833. if(type == "single")
  834. {
  835. escapedSubmission.joke = htmlEscape(submission.joke);
  836. }
  837. else if(type == "twopart")
  838. {
  839. escapedSubmission.setup = htmlEscape(submission.setup);
  840. escapedSubmission.delivery = htmlEscape(submission.delivery);
  841. }
  842. subDisp.innerText = JSON.stringify(escapedSubmission, null, 4);
  843. var subCodeElem = gebid("submissionCodeElement");
  844. if(!subCodeElem.classList.contains("prettyprint"))
  845. {
  846. subCodeElem.classList.add("prettyprint");
  847. }
  848. if(subCodeElem.classList.contains("prettyprinted"))
  849. {
  850. subCodeElem.classList.remove("prettyprinted");
  851. }
  852. if(subCodeElem.classList.contains("lang-json"))
  853. {
  854. subCodeElem.classList.remove("lang-json");
  855. }
  856. subCodeElem.classList.add("lang-json");
  857. setTimeout(function() {
  858. PR.prettyPrint(); // eslint-disable-line no-undef
  859. }, 5);
  860. }
  861. /**
  862. * Escapes unsafe HTML
  863. * @param {String} unsafeHTML
  864. * @returns {String}
  865. */
  866. function htmlEscape(unsafeHTML)
  867. {
  868. unsafeHTML = unsafeHTML.replace(/</g, "&lt;");
  869. unsafeHTML = unsafeHTML.replace(/>/g, "&gt;");
  870. return unsafeHTML;
  871. }
  872. //#MARKER privacy policy
  873. function privPolMoreInfo()
  874. {
  875. sMenu.open("privacyPolicy");
  876. }
  877. function hideUsageTerms()
  878. {
  879. gebid("usageTerms").style.display = "none";
  880. Cookies.set("hideUsageTerms", "true", {"expires": 365}); // eslint-disable-line no-undef
  881. }
  882. //#MARKER misc
  883. function openRestartForm()
  884. {
  885. sMenu.open("restartPrompt");
  886. }
  887. function submitRestartForm()
  888. {
  889. restart(gebid("restartFormToken").value || null);
  890. sMenu.close("restartPrompt");
  891. }
  892. function restart(token)
  893. {
  894. if(!token)
  895. {
  896. token = prompt("Enter restart token:");
  897. }
  898. if(!token)
  899. {
  900. return;
  901. }
  902. var restartXhr = new XMLHttpRequest();
  903. restartXhr.open("POST", settings.baseURL + "/restart");
  904. restartXhr.onreadystatechange = function() {
  905. if(restartXhr.readyState == 4)
  906. {
  907. if(restartXhr.status == 400)
  908. {
  909. console.warn("Error 400 - The entered token is invalid");
  910. alert("Error 400 - The entered token is invalid");
  911. }
  912. else if(restartXhr.status >= 300)
  913. {
  914. console.warn("Error " + restartXhr.status + " - " + restartXhr.responseText);
  915. alert("Error " + restartXhr.status + " - " + restartXhr.responseText);
  916. }
  917. else if(restartXhr.status < 300)
  918. {
  919. var xhrData = JSON.parse(restartXhr.responseText);
  920. console.info(xhrData.message + "\nInternal Time of Restart: " + toFormattedDate(xhrData.timestamp));
  921. alert(xhrData.message + "\nInternal Time of Restart: " + toFormattedDate(xhrData.timestamp));
  922. }
  923. }
  924. };
  925. restartXhr.send(token.toString());
  926. }
  927. function toFormattedDate(unixTimestamp)
  928. {
  929. var d = new Date(unixTimestamp);
  930. return d.toLocaleString("de-DE");
  931. }
  932. /**
  933. * Parses the contributor object and puts the contents into the element #contributorsContainer
  934. */
  935. function loadContributors()
  936. {
  937. var container = gebid("contributorsContainer");
  938. container.innerHTML = "";
  939. settings.contributorsObject.forEach(function(contributor) {
  940. var contrElem = document.createElement("div");
  941. contrElem.classList.add("contributor");
  942. var name = document.createElement("div");
  943. name.classList.add("contributorName");
  944. name.innerText = contributor.name;
  945. contrElem.appendChild(name);
  946. if(typeof contributor.email == "string")
  947. {
  948. var email = document.createElement("a");
  949. email.href = "mailto:" + contributor.email + "?subject=JokeAPI%20Contribution";
  950. email.innerText = contributor.email;
  951. email.classList.add("contributorEmail");
  952. email.classList.add("contributorContact");
  953. contrElem.appendChild(email);
  954. }
  955. if(typeof contributor.url == "string")
  956. {
  957. var url = document.createElement("a");
  958. url.href = contributor.url;
  959. url.innerText = contributor.url;
  960. url.classList.add("contributorURL");
  961. url.classList.add("contributorContact");
  962. contrElem.appendChild(url);
  963. }
  964. if(Array.isArray(contributor.contributions))
  965. {
  966. var contributionList = document.createElement("ul");
  967. contributionList.classList.add("contributorContributionsList");
  968. contributor.contributions.forEach(function(contribution) {
  969. var contributionEl = document.createElement("li");
  970. contributionEl.innerText = contribution;
  971. contributionList.appendChild(contributionEl);
  972. });
  973. contrElem.appendChild(contributionList);
  974. }
  975. container.appendChild(contrElem);
  976. });
  977. /*
  978. [
  979. {
  980. "name": "XY",
  981. "email": "[email protected]", (OPTIONAL)
  982. "url": "https://example.org", (OPTIONAL)
  983. "contributions": [
  984. "Implemented a whole lot of stuff",
  985. "Stuff",
  986. "Even more stuff",
  987. ...
  988. ]
  989. },
  990. ...
  991. ]
  992. */
  993. }
  994. /**
  995. * Loads all category aliases, adding them to the element #catAliasesContainer
  996. */
  997. function loadCategoryAliases()
  998. {
  999. var container = gebid("catAliasesContainer");
  1000. var amt = 0;
  1001. Object.keys(settings.categoryAliasesObject).forEach(function(key) {
  1002. var value = settings.categoryAliasesObject[key];
  1003. var trElem = document.createElement("tr");
  1004. var keyElem = document.createElement("td");
  1005. keyElem.innerText = key;
  1006. var valElem = document.createElement("td");
  1007. valElem.innerText = value;
  1008. trElem.appendChild(keyElem);
  1009. trElem.appendChild(valElem);
  1010. container.appendChild(trElem);
  1011. amt++;
  1012. });
  1013. console.info("Found " + amt + " category aliases");
  1014. }
  1015. //#MARKER cleanup
  1016. function unused(...args)
  1017. {
  1018. args.forEach(function(arg) {
  1019. try{arg.toString();}
  1020. catch(err) {return;}
  1021. return;
  1022. });
  1023. }
  1024. unused(openNav, closeNav, onLoad, reRender, privPolMoreInfo, hideUsageTerms, sendTryItRequest, submitRestartForm);