index.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  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. try
  138. {
  139. var fileFormats = JSON.parse('<!--%#INSERT:FILEFORMATARRAY#%-->');
  140. if(fileFormats.includes("JSON"))
  141. {
  142. fileFormats.splice(fileFormats.indexOf("JSON"), 1);
  143. }
  144. Array.from(document.getElementsByClassName("insFormatsS")).forEach(function(el) {
  145. el.innerText = fileFormats.join(" and ");
  146. });
  147. var flags = JSON.parse('<!--%#INSERT:FLAGSARRAY#%-->');
  148. Array.from(document.getElementsByClassName("insFlags")).forEach(function(el) {
  149. el.innerText = flags.join(", ");
  150. });
  151. var formats = JSON.parse('<!--%#INSERT:FILEFORMATARRAY#%-->');
  152. Array.from(document.getElementsByClassName("insFormats")).forEach(function(el) {
  153. el.innerText = formats.join(", ").toLowerCase();
  154. });
  155. var categories = JSON.parse('<!--%#INSERT:CATEGORYARRAY#%-->');
  156. Array.from(document.getElementsByClassName("insCategories")).forEach(function(el) {
  157. el.innerText = categories.join(", ");
  158. });
  159. }
  160. catch(err)
  161. {
  162. return alert("Documentation compilation was unsuccessful: Value insertion error:\n" + err);
  163. }
  164. //#SECTION submission form
  165. var inputElems = [
  166. "f_category",
  167. "f_type",
  168. "f_flags_nsfw",
  169. "f_flags_religious",
  170. "f_flags_political",
  171. "f_flags_racist",
  172. "f_flags_sexist",
  173. "f_flags_explicit",
  174. "f_setup",
  175. "f_delivery",
  176. "f_language"
  177. ];
  178. for(var ii = 0; ii < inputElems.length; ii++)
  179. {
  180. var elm = gebid(inputElems[ii]);
  181. if(elm.tagName.toLowerCase() != "textarea")
  182. {
  183. elm.onchange = function()
  184. {
  185. return valChanged(this);
  186. }
  187. }
  188. else
  189. {
  190. elm.oninput = function()
  191. {
  192. return valChanged(this);
  193. }
  194. }
  195. }
  196. var langXhr = new XMLHttpRequest();
  197. var langUrl = settings.baseURL + "/languages";
  198. var langSelectElems = document.getElementsByClassName("appendLangOpts");
  199. var otherOpt = document.createElement("option");
  200. otherOpt.innerText = "Other / Custom";
  201. otherOpt.value = "other";
  202. gebid("f_language").appendChild(otherOpt);
  203. var sysLangsText = "";
  204. var jokeLangsText = "";
  205. langXhr.open("GET", langUrl);
  206. langXhr.onreadystatechange = function() {
  207. var xErrElem;
  208. if(langXhr.readyState == 4 && langXhr.status < 300)
  209. {
  210. var respJSON = JSON.parse(langXhr.responseText.toString());
  211. var languagesArray = respJSON.jokeLanguages;
  212. sysLangsText = respJSON.systemLanguages.join(", ");
  213. jokeLangsText = respJSON.jokeLanguages.join(", ");
  214. for(var i = 0; i < languagesArray.length; i++)
  215. {
  216. for(var j = 0; j < langSelectElems.length; j++)
  217. {
  218. var langName = "";
  219. for(var k = 0; k < respJSON.possibleLanguages.length; k++)
  220. {
  221. if(respJSON.possibleLanguages[k].code == languagesArray[i])
  222. {
  223. langName = respJSON.possibleLanguages[k].name;
  224. }
  225. }
  226. xErrElem = document.createElement("option");
  227. xErrElem.value = languagesArray[i];
  228. if(languagesArray[i] == settings.defaultLang)
  229. {
  230. xErrElem.selected = true;
  231. }
  232. xErrElem.innerText = languagesArray[i] + " - " + langName;
  233. if(languagesArray[i] == settings.defaultLang)
  234. xErrElem.selected = true;
  235. langSelectElems[j].appendChild(xErrElem);
  236. langSelectElems[j].value = settings.defaultLang;
  237. }
  238. }
  239. var sysLangsElems = document.getElementsByClassName("insSysLangs");
  240. var jokeLangsElems = document.getElementsByClassName("insJokeLangs");
  241. for(var sI = 0; sI < sysLangsElems.length; sI++)
  242. {
  243. sysLangsElems[sI].innerText = sysLangsText;
  244. }
  245. for(var jI = 0; jI < sysLangsElems.length; jI++)
  246. {
  247. jokeLangsElems[jI].innerText = jokeLangsText;
  248. }
  249. }
  250. else if(langXhr.readyState == 4 && langXhr.status >= 300)
  251. {
  252. for(var ii = 0; ii < langSelectElems.length; ii++)
  253. {
  254. xErrElem = document.createElement("option");
  255. xErrElem.value = "en";
  256. xErrElem.innerText = "en - English";
  257. xErrElem.selected = true;
  258. langSelectElems[ii].appendChild(xErrElem);
  259. }
  260. }
  261. };
  262. langXhr.send();
  263. var infoXhr = new XMLHttpRequest();
  264. infoXhr.open("GET", (settings.baseURL + "/info"));
  265. infoXhr.onreadystatechange = function() {
  266. if(infoXhr.readyState == 4 && infoXhr.status < 300)
  267. {
  268. var respJSON = JSON.parse(infoXhr.responseText.toString());
  269. var idrKeys = Object.keys(respJSON.jokes.idRange);
  270. for(var i = 0; i < idrKeys.length; i++)
  271. {
  272. var idrKey = idrKeys[i];
  273. idRanges[idrKey] = respJSON.jokes.idRange[idrKey];
  274. try
  275. {
  276. console.info("<!--%#INSERT:NAME#%--> is serving " + (respJSON.jokes.idRange[idrKey][1] + 1) + " jokes from language \"" + idrKey + "\"");
  277. }
  278. catch(err)
  279. {
  280. void(err);
  281. }
  282. }
  283. reRender();
  284. }
  285. else if(infoXhr.readyState == 4 && infoXhr.status >= 300)
  286. {
  287. console.error("Couldn't get ID range of all languages. Defaulting to the max possible value.");
  288. }
  289. };
  290. infoXhr.send();
  291. gebid("submitBtn").addEventListener("click", function() {
  292. submitJoke();
  293. });
  294. buildSubmission();
  295. setTimeout(function() { buildSubmission() }, 2000);
  296. gebid("insUserAgent").innerText = navigator.userAgent;
  297. gebid("lcodeSelect").value = settings.defaultLang;
  298. var abElems = document.getElementsByClassName("antiBotE");
  299. for(var l = 0; l < abElems.length; l++)
  300. {
  301. abElems[l].onclick = function()
  302. {
  303. if(!this.classList.contains("shown"))
  304. {
  305. this.innerText = atob(this.dataset.enc);
  306. this.classList.add("shown");
  307. }
  308. }
  309. }
  310. gebid("lcodeSelect").value = settings.defaultLang;
  311. gebid("lcodeSelect").onchange = function() { reRender(true) };
  312. gebid("sideNavOpen").onclick = function() { return openNav(); };
  313. loadCategoryAliases();
  314. loadContributors();
  315. }
  316. /**
  317. * @param {MouseEvent} e
  318. */
  319. function tryCloseSideNav(e)
  320. {
  321. if(document.body.dataset["sidenav"] == "opened")
  322. {
  323. e.preventDefault();
  324. closeNav();
  325. }
  326. }
  327. function addCodeTabs()
  328. {
  329. var codeElements = document.getElementsByTagName("code");
  330. for(var i = 0; i < codeElements; i++)
  331. {
  332. if(codeElements[i].classList.contains("prettyprint"))
  333. codeElements[i].innerHTML = codeElements[i].innerHTML.replace(/&tab;/gm, "&nbsp;&nbsp;&nbsp;&nbsp;");
  334. }
  335. }
  336. //#MARKER SideNav
  337. function openNav()
  338. {
  339. console.info("opening nav");
  340. setTimeout(function() {
  341. document.body.dataset["sidenav"] = "opened";
  342. }, 50);
  343. window.jokeapi.sidenavOpened = true;
  344. gebid("sidenav").style.width = "280px";
  345. gebid("content").style.marginLeft = "280px";
  346. document.getElementsByTagName("header")[0].dataset["grayscaled"] = "true";
  347. gebid("sideNavOpen").style.visibility = "hidden";
  348. }
  349. function closeNav()
  350. {
  351. console.info("closing nav");
  352. if(document.body.dataset["sidenav"] != "opened")
  353. return;
  354. window.jokeapi.sidenavOpened = false;
  355. document.body.dataset["sidenav"] = "closed";
  356. gebid("sidenav").style.width = "0";
  357. gebid("content").style.marginLeft = "10px";
  358. document.getElementsByTagName("header")[0].dataset["grayscaled"] = "false";
  359. gebid("sideNavOpen").style.visibility = "visible";
  360. }
  361. // function getQueryStringObject()
  362. // {
  363. // var qstrObj = {};
  364. // if(!window.location.href.includes("?"))
  365. // return null;
  366. // var rawQstr = window.location.href.split("?")[1];
  367. // var qstrArr = [];
  368. // if(rawQstr.includes("#"))
  369. // rawQstr = rawQstr.split("#")[0];
  370. // if(rawQstr != null && rawQstr.includes("&"))
  371. // qstrArr = rawQstr.split("&");
  372. // else if(rawQstr != null)
  373. // qstrArr = [rawQstr];
  374. // else return null;
  375. // if(qstrArr.length > 0)
  376. // qstrArr.forEach(function(qstrEntry) {
  377. // if(qstrEntry.includes("="))
  378. // qstrObj[qstrEntry.split("=")[0]] = qstrEntry.split("=")[1];
  379. // });
  380. // else return null;
  381. // return qstrObj;
  382. // }
  383. /**
  384. * @param {Boolean} [langChanged]
  385. */
  386. function reRender(langChanged)
  387. {
  388. console.info("Re-rendering try-it form");
  389. var allOk = true;
  390. //#SECTION category
  391. var isValid = false;
  392. document.getElementsByName("catSelect").forEach(function(el) {
  393. if(!el.checked)
  394. return;
  395. if(el.value == "any")
  396. {
  397. isValid = true;
  398. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  399. gebid(cat).disabled = true;
  400. });
  401. }
  402. else
  403. {
  404. var isChecked = false;
  405. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  406. var cel = gebid(cat);
  407. cel.disabled = false;
  408. if(cel.checked)
  409. isChecked = true;
  410. });
  411. if(isChecked)
  412. isValid = true;
  413. }
  414. });
  415. if(!isValid)
  416. {
  417. allOk = false;
  418. gebid("categoryWrapper").style.borderColor = "red";
  419. }
  420. else
  421. {
  422. gebid("categoryWrapper").style.borderColor = "initial";
  423. }
  424. //#SECTION format
  425. if(!gebid("typ-cb1").checked && !gebid("typ-cb2").checked)
  426. {
  427. allOk = false;
  428. gebid("typeSelectWrapper").style.borderColor = "red";
  429. }
  430. else
  431. {
  432. gebid("typeSelectWrapper").style.borderColor = "initial";
  433. }
  434. //#SECTION id range
  435. if(langChanged === true)
  436. {
  437. console.warn("langchanged")
  438. var langCode = gebid("lcodeSelect").value;
  439. if(idRanges[langCode])
  440. {
  441. var maxRange = parseInt(idRanges[langCode][1]);
  442. gebid("idRangeInputTo").max = maxRange;
  443. gebid("idRangeInputTo").value = maxRange;
  444. maxJokeIdRange = maxRange;
  445. }
  446. else
  447. {
  448. gebid("idRangeInputTo").max = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  449. maxJokeIdRange = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  450. }
  451. }
  452. var numRegex = /^[0-9]+$/gm;
  453. var fromVal = gebid("idRangeInputFrom").value;
  454. var toVal = gebid("idRangeInputTo").value;
  455. var fromValInt = parseInt(fromVal);
  456. var toValInt = parseInt(toVal);
  457. var outOfRange = (fromValInt < 0 || toValInt > maxJokeIdRange);
  458. var notNumber = ((fromVal.match(numRegex) == null) || (toVal.match(numRegex) == null));
  459. if(outOfRange || notNumber || fromValInt > toValInt)
  460. {
  461. allOk = false;
  462. gebid("idRangeWrapper").style.borderColor = "red";
  463. }
  464. else
  465. {
  466. gebid("idRangeWrapper").style.borderColor = "initial";
  467. }
  468. var jokesAmount = parseInt(gebid("jokesAmountInput").value);
  469. if(jokesAmount > parseInt("<!--%#INSERT:MAXJOKEAMOUNT#%-->") || jokesAmount < 1 || isNaN(jokesAmount))
  470. {
  471. allOk = false;
  472. gebid("jokeAmountWrapper").style.borderColor = "red";
  473. }
  474. else
  475. {
  476. gebid("jokeAmountWrapper").style.borderColor = "initial";
  477. }
  478. if(allOk)
  479. {
  480. tryItOk = true;
  481. }
  482. else
  483. {
  484. tryItOk = false;
  485. }
  486. buildURL();
  487. }
  488. //#MARKER build URL
  489. function buildURL()
  490. {
  491. var queryParams = [];
  492. //#SECTION categories
  493. var selectedCategories = [settings.anyCategoryName];
  494. if(gebid("cat-radio2").checked)
  495. {
  496. selectedCategories = [];
  497. if(gebid("cat-cb1").checked)
  498. {
  499. selectedCategories.push("Programming");
  500. }
  501. if(gebid("cat-cb2").checked)
  502. {
  503. selectedCategories.push("Miscellaneous");
  504. }
  505. if(gebid("cat-cb3").checked)
  506. {
  507. selectedCategories.push("Dark");
  508. }
  509. if(gebid("cat-cb4").checked)
  510. {
  511. selectedCategories.push("Pun");
  512. }
  513. if(gebid("cat-cb5").checked)
  514. {
  515. selectedCategories.push("Spooky");
  516. }
  517. if(gebid("cat-cb6").checked)
  518. {
  519. selectedCategories.push("Christmas");
  520. }
  521. if(selectedCategories.length == 0)
  522. {
  523. selectedCategories.push(settings.anyCategoryName);
  524. }
  525. }
  526. //#SECTION language
  527. var langCode = gebid("lcodeSelect").value || settings.defaultLang;
  528. if(langCode != settings.defaultLang)
  529. queryParams.push("lang=" + langCode);
  530. //#SECTION flags
  531. var flagElems = [gebid("blf-cb1"), gebid("blf-cb2"), gebid("blf-cb3"), gebid("blf-cb4"), gebid("blf-cb5"), gebid("blf-cb6")];
  532. var flagNames = JSON.parse('<!--%#INSERT:FLAGSARRAY#%-->');
  533. var selectedFlags = [];
  534. flagElems.forEach(function(el, i) {
  535. if(el.checked)
  536. {
  537. selectedFlags.push(flagNames[i]);
  538. }
  539. });
  540. if(selectedFlags.length > 0)
  541. {
  542. queryParams.push("blacklistFlags=" + selectedFlags.join(","));
  543. }
  544. //#SECTION format
  545. var formatElems = [gebid("fmt-cb1"), gebid("fmt-cb2"), gebid("fmt-cb3"), gebid("fmt-cb4")];
  546. formatElems.forEach(function(el) {
  547. if(el.checked && el.value != settings.defaultFormat)
  548. {
  549. queryParams.push("format=" + el.value);
  550. }
  551. });
  552. //#SECTION type
  553. var singleJoke = gebid("typ-cb1").checked;
  554. var twopartJoke = gebid("typ-cb2").checked;
  555. if(singleJoke ^ twopartJoke == 1)
  556. {
  557. if(singleJoke)
  558. {
  559. queryParams.push("type=single");
  560. }
  561. else if(twopartJoke)
  562. {
  563. queryParams.push("type=twopart");
  564. }
  565. }
  566. //#SECTION search string
  567. var sstr = gebid("searchStringInput").value;
  568. if(sstr)
  569. {
  570. queryParams.push("contains=" + encodeURIComponent(sstr));
  571. }
  572. //#SECTION id range
  573. var range = [parseInt(gebid("idRangeInputFrom").value), parseInt(gebid("idRangeInputTo").value)];
  574. if(!isNaN(range[0]) && !isNaN(range[1]) && range[0] >= 0 && range[1] <= maxJokeIdRange && range[1] >= range[0])
  575. {
  576. if(range[0] == range[1] && range[0] >= 0 && range[0] <= maxJokeIdRange)
  577. {
  578. // Use "x" format
  579. queryParams.push("idRange=" + range[0]);
  580. }
  581. else if(range[0] != 0 || range[1] != maxJokeIdRange)
  582. {
  583. // Use "x-y" format
  584. queryParams.push("idRange=" + range[0] + "-" + range[1]);
  585. }
  586. }
  587. //#SECTION amount
  588. var jokeAmount = parseInt(gebid("jokesAmountInput").value);
  589. if(jokeAmount != 1 && !isNaN(jokeAmount) && jokeAmount > 0 && jokeAmount <= parseInt("<!--%#INSERT:MAXJOKEAMOUNT#%-->"))
  590. {
  591. queryParams.push("amount=" + jokeAmount);
  592. }
  593. tryItURL = settings.baseURL + "/" + settings.jokeEndpoint + "/" + selectedCategories.join(",");
  594. if(queryParams.length > 0)
  595. {
  596. tryItURL += "?" + queryParams.join("&");
  597. }
  598. gebid("urlBuilderUrl").innerText = tryItURL;
  599. }
  600. //#MARKER send request
  601. function sendTryItRequest()
  602. {
  603. var sendStartTimestamp = new Date().getTime();
  604. var prpr = gebid("urlBuilderPrettyprint");
  605. var tryItRequestError = function(err) {
  606. if(prpr.classList.contains("prettyprint"))
  607. {
  608. prpr.classList.remove("prettyprint");
  609. }
  610. if(prpr.classList.contains("prettyprinted"))
  611. {
  612. prpr.classList.remove("prettyprinted");
  613. }
  614. gebid("tryItResult").innerHTML = "Error:<br><br>" + err;
  615. }
  616. if(!tryItOk)
  617. {
  618. 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.");
  619. }
  620. if(!prpr.classList.contains("prettyprint"))
  621. {
  622. prpr.classList.add("prettyprint");
  623. }
  624. if(prpr.classList.contains("prettyprinted"))
  625. {
  626. prpr.classList.remove("prettyprinted");
  627. }
  628. if(prpr.classList.contains("lang-json"))
  629. {
  630. prpr.classList.remove("lang-json");
  631. }
  632. if(prpr.classList.contains("lang-yaml"))
  633. {
  634. prpr.classList.remove("lang-yaml");
  635. }
  636. if(prpr.classList.contains("lang-xml"))
  637. {
  638. prpr.classList.remove("lang-xml");
  639. }
  640. prpr.classList.add("lang-json");
  641. try
  642. {
  643. var xhr = new XMLHttpRequest();
  644. xhr.open("GET", tryItURL);
  645. xhr.onreadystatechange = function() {
  646. if(xhr.readyState == 4 && (xhr.status < 300 || xhr.status == 429))
  647. {
  648. var result = "";
  649. if(xhr.getResponseHeader("content-type").includes("json"))
  650. {
  651. result = JSON.stringify(JSON.parse(xhr.responseText.toString()), null, 4);
  652. }
  653. else
  654. {
  655. if(xhr.getResponseHeader("content-type").includes("xml"))
  656. {
  657. gebid("urlBuilderPrettyprint").classList.remove("lang-json");
  658. gebid("urlBuilderPrettyprint").classList.add("lang-xml");
  659. }
  660. else
  661. {
  662. gebid("urlBuilderPrettyprint").classList.remove("lang-json");
  663. gebid("urlBuilderPrettyprint").classList.add("lang-yaml");
  664. }
  665. result = xhr.responseText.toString();
  666. result = result.replace(/[<]/gm, "&lt;");
  667. result = result.replace(/[>]/gm, "&gt;");
  668. }
  669. gebid("tryItResult").innerText = result;
  670. gebid("tryItFormLatency").innerText = "Latency: " + (new Date().getTime() - sendStartTimestamp) + " ms";
  671. PR.prettyPrint(); // eslint-disable-line no-undef
  672. }
  673. else
  674. {
  675. tryItRequestError(xhr.responseText);
  676. }
  677. }
  678. xhr.send();
  679. }
  680. catch(err)
  681. {
  682. tryItRequestError(err);
  683. }
  684. }
  685. //#MARKER interactive elements
  686. function resetTryItForm(confirmation)
  687. {
  688. if(confirmation === true && !confirm("Do you really want to reset the form?"))
  689. return;
  690. ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4", "cat-cb5", "cat-cb6"].forEach(function(cat) {
  691. gebid(cat).checked = false;
  692. });
  693. gebid("cat-radio1").checked = true;
  694. ["blf-cb1", "blf-cb2", "blf-cb3", "blf-cb4", "blf-cb5", "blf-cb6"].forEach(function(flg) {
  695. gebid(flg).checked = false;
  696. });
  697. gebid("fmt-cb1").checked = true;
  698. ["typ-cb1", "typ-cb2"].forEach(function(type) {
  699. gebid(type).checked = true;
  700. });
  701. gebid("searchStringInput").value = "";
  702. gebid("idRangeInputFrom").value = 0;
  703. gebid("idRangeInputTo").value = parseInt("<!--%#INSERT:TOTALJOKESZEROINDEXED#%-->");
  704. gebid("jokesAmountInput").value = 1;
  705. reRender();
  706. }
  707. //#MARKER submit joke
  708. function submitJoke()
  709. {
  710. var submitBtn = gebid("submitBtn");
  711. if(submitBtn.disabled == true)
  712. {
  713. return;
  714. }
  715. var xhr = new XMLHttpRequest();
  716. xhr.open("POST", settings.submitUrl);
  717. xhr.onreadystatechange = function() {
  718. if(xhr.readyState == 4)
  719. {
  720. var res;
  721. try
  722. {
  723. res = JSON.parse(xhr.responseText);
  724. }
  725. catch(err)
  726. {
  727. alert("Error " + res.status + " while sending your submission:\n" + res.message + (res.additionalInfo ? "\n\nAdditional info:\n" + res.additionalInfo : ""));
  728. return;
  729. }
  730. if(xhr.status < 300)
  731. {
  732. if(res.error == false)
  733. {
  734. submitBtn.disabled = true;
  735. alert(res.message);
  736. setTimeout(function() {
  737. gebid("submitBtn").disabled = false;
  738. }, 2000);
  739. }
  740. else if(res.error == true)
  741. {
  742. alert("Error " + res.status + " while sending your submission:\n" + res.message + (res.additionalInfo ? "\n\nAdditional info:\n" + res.additionalInfo : ""));
  743. }
  744. }
  745. else if(xhr.status >= 300)
  746. {
  747. var addInfo = res.message;
  748. if(res.additionalInfo)
  749. {
  750. addInfo = res.additionalInfo;
  751. }
  752. alert("Error while sending your submission:\n" + addInfo);
  753. }
  754. }
  755. };
  756. xhr.send(JSON.stringify(submission, null, 4));
  757. }
  758. function valChanged(element)
  759. {
  760. if(element.id == "f_type")
  761. {
  762. if(element.value == "single")
  763. {
  764. gebid("f_setup").placeholder = "Joke";
  765. gebid("f_delivery").style.display = "none";
  766. }
  767. else if(element.value == "twopart")
  768. {
  769. gebid("f_setup").placeholder = "Setup";
  770. gebid("f_delivery").style.display = "initial";
  771. }
  772. }
  773. if(element.id == "f_language")
  774. {
  775. if(element.value == "other")
  776. {
  777. gebid("f_langHideContainer").classList.remove("hidden");
  778. }
  779. else
  780. {
  781. gebid("f_langHideContainer").classList.add("hidden");
  782. }
  783. }
  784. buildSubmission();
  785. }
  786. function buildSubmission()
  787. {
  788. var category = gebid("f_category").value;
  789. var type = gebid("f_type").value;
  790. submission = {
  791. formatVersion: settings.formatVersion,
  792. category: category,
  793. type: type
  794. }
  795. if(type == "single")
  796. {
  797. submission.joke = gebid("f_setup").value;
  798. }
  799. else if(type == "twopart")
  800. {
  801. submission.setup = gebid("f_setup").value;
  802. submission.delivery = gebid("f_delivery").value;
  803. }
  804. var sLang = gebid("f_language").value || settings.defaultLang;
  805. if(sLang == "other")
  806. {
  807. var elVal = gebid("f_customLang").value;
  808. if(elVal && elVal.length == 2)
  809. {
  810. sLang = elVal;
  811. }
  812. else
  813. {
  814. sLang = "Please enter 2 char language code";
  815. }
  816. }
  817. submission = {
  818. ...submission,
  819. flags: {
  820. nsfw: gebid("f_flags_nsfw").checked,
  821. religious: gebid("f_flags_religious").checked,
  822. political: gebid("f_flags_political").checked,
  823. racist: gebid("f_flags_racist").checked,
  824. sexist: gebid("f_flags_sexist").checked,
  825. explicit: gebid("f_flags_explicit").checked
  826. },
  827. lang: sLang
  828. };
  829. var subDisp = gebid("submissionDisplay");
  830. var escapedSubmission = JSON.parse(JSON.stringify(submission)); // copy value without reference
  831. if(type == "single")
  832. {
  833. escapedSubmission.joke = htmlEscape(submission.joke);
  834. }
  835. else if(type == "twopart")
  836. {
  837. escapedSubmission.setup = htmlEscape(submission.setup);
  838. escapedSubmission.delivery = htmlEscape(submission.delivery);
  839. }
  840. subDisp.innerText = JSON.stringify(escapedSubmission, null, 4);
  841. var subCodeElem = gebid("submissionCodeElement");
  842. if(!subCodeElem.classList.contains("prettyprint"))
  843. {
  844. subCodeElem.classList.add("prettyprint");
  845. }
  846. if(subCodeElem.classList.contains("prettyprinted"))
  847. {
  848. subCodeElem.classList.remove("prettyprinted");
  849. }
  850. if(subCodeElem.classList.contains("lang-json"))
  851. {
  852. subCodeElem.classList.remove("lang-json");
  853. }
  854. subCodeElem.classList.add("lang-json");
  855. setTimeout(function() {
  856. PR.prettyPrint(); // eslint-disable-line no-undef
  857. }, 5);
  858. }
  859. /**
  860. * Escapes unsafe HTML
  861. * @param {String} unsafeHTML
  862. * @returns {String}
  863. */
  864. function htmlEscape(unsafeHTML)
  865. {
  866. unsafeHTML = unsafeHTML.replace(/</g, "&lt;");
  867. unsafeHTML = unsafeHTML.replace(/>/g, "&gt;");
  868. return unsafeHTML;
  869. }
  870. //#MARKER privacy policy
  871. function privPolMoreInfo()
  872. {
  873. sMenu.open("privacyPolicy");
  874. }
  875. function hideUsageTerms()
  876. {
  877. gebid("usageTerms").style.display = "none";
  878. Cookies.set("hideUsageTerms", "true", {"expires": 365}); // eslint-disable-line no-undef
  879. }
  880. //#MARKER misc
  881. function openRestartForm()
  882. {
  883. sMenu.open("restartPrompt");
  884. }
  885. function submitRestartForm()
  886. {
  887. restart(gebid("restartFormToken").value || null);
  888. sMenu.close("restartPrompt");
  889. }
  890. function restart(token)
  891. {
  892. if(!token)
  893. {
  894. token = prompt("Enter restart token:");
  895. }
  896. if(!token)
  897. {
  898. return;
  899. }
  900. var restartXhr = new XMLHttpRequest();
  901. restartXhr.open("POST", settings.baseURL + "/restart");
  902. restartXhr.onreadystatechange = function() {
  903. if(restartXhr.readyState == 4)
  904. {
  905. if(restartXhr.status == 400)
  906. {
  907. console.warn("Error 400 - The entered token is invalid");
  908. alert("Error 400 - The entered token is invalid");
  909. }
  910. else if(restartXhr.status >= 300)
  911. {
  912. console.warn("Error " + restartXhr.status + " - " + restartXhr.responseText);
  913. alert("Error " + restartXhr.status + " - " + restartXhr.responseText);
  914. }
  915. else if(restartXhr.status < 300)
  916. {
  917. var xhrData = JSON.parse(restartXhr.responseText);
  918. console.info(xhrData.message + "\nInternal Time of Restart: " + toFormattedDate(xhrData.timestamp));
  919. alert(xhrData.message + "\nInternal Time of Restart: " + toFormattedDate(xhrData.timestamp));
  920. }
  921. }
  922. };
  923. restartXhr.send(token.toString());
  924. }
  925. function toFormattedDate(unixTimestamp)
  926. {
  927. var d = new Date(unixTimestamp);
  928. return d.toLocaleString("de-DE");
  929. }
  930. /**
  931. * Parses the contributor object and puts the contents into the element #contributorsContainer
  932. */
  933. function loadContributors()
  934. {
  935. var container = gebid("contributorsContainer");
  936. container.innerHTML = "";
  937. settings.contributorsObject.forEach(function(contributor) {
  938. var contrElem = document.createElement("div");
  939. contrElem.classList.add("contributor");
  940. var name = document.createElement("div");
  941. name.classList.add("contributorName");
  942. name.innerText = contributor.name;
  943. contrElem.appendChild(name);
  944. if(typeof contributor.email == "string")
  945. {
  946. var email = document.createElement("a");
  947. email.href = "mailto:" + contributor.email + "?subject=JokeAPI%20Contribution";
  948. email.innerText = contributor.email;
  949. email.classList.add("contributorEmail");
  950. email.classList.add("contributorContact");
  951. contrElem.appendChild(email);
  952. }
  953. if(typeof contributor.url == "string")
  954. {
  955. var url = document.createElement("a");
  956. url.href = contributor.url;
  957. url.innerText = contributor.url;
  958. url.classList.add("contributorURL");
  959. url.classList.add("contributorContact");
  960. contrElem.appendChild(url);
  961. }
  962. if(Array.isArray(contributor.contributions))
  963. {
  964. var contributionList = document.createElement("ul");
  965. contributionList.classList.add("contributorContributionsList");
  966. contributor.contributions.forEach(function(contribution) {
  967. var contributionEl = document.createElement("li");
  968. contributionEl.innerText = contribution;
  969. contributionList.appendChild(contributionEl);
  970. });
  971. contrElem.appendChild(contributionList);
  972. }
  973. container.appendChild(contrElem);
  974. });
  975. /*
  976. [
  977. {
  978. "name": "XY",
  979. "email": "[email protected]", (OPTIONAL)
  980. "url": "https://example.org", (OPTIONAL)
  981. "contributions": [
  982. "Implemented a whole lot of stuff",
  983. "Stuff",
  984. "Even more stuff",
  985. ...
  986. ]
  987. },
  988. ...
  989. ]
  990. */
  991. }
  992. /**
  993. * Loads all category aliases, adding them to the element #catAliasesContainer
  994. */
  995. function loadCategoryAliases()
  996. {
  997. var container = gebid("catAliasesContainer");
  998. var amt = 0;
  999. Object.keys(settings.categoryAliasesObject).forEach(function(key) {
  1000. var value = settings.categoryAliasesObject[key];
  1001. var trElem = document.createElement("tr");
  1002. var keyElem = document.createElement("td");
  1003. keyElem.innerText = key;
  1004. var valElem = document.createElement("td");
  1005. valElem.innerText = value;
  1006. trElem.appendChild(keyElem);
  1007. trElem.appendChild(valElem);
  1008. container.appendChild(trElem);
  1009. amt++;
  1010. });
  1011. console.info("Found " + amt + " category aliases");
  1012. }
  1013. //#MARKER cleanup
  1014. function unused(...args)
  1015. {
  1016. args.forEach(function(arg) {
  1017. try{arg.toString();}
  1018. catch(err) {return;}
  1019. return;
  1020. });
  1021. }
  1022. unused(openNav, closeNav, onLoad, reRender, privPolMoreInfo, hideUsageTerms, sendTryItRequest, submitRestartForm);