E(X)Hentai Helper

Links between E-Hentai and ExHentai page, and also links user to ExHentai automatically if gallery is "removed" and adds "view later" function

  1. // ==UserScript==
  2. // @name E(X)Hentai Helper
  3. // @description Links between E-Hentai and ExHentai page, and also links user to ExHentai automatically if gallery is "removed" and adds "view later" function
  4. // @namespace https://greasyfork.org/en/scripts/24342-e-hentai-exhentai
  5. // @version 4.05
  6. // @icon https://e-hentai.org/favicon.ico
  7. // @resource exCSS http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css
  8. // @resource jqueryui https://code.jquery.com/ui/1.12.0/jquery-ui.min.js
  9.  
  10. // @include https://upload.e-hentai.org/*
  11. // @include http*://e-hentai.org/*
  12. // @include http*://exhentai.org/*
  13.  
  14. // @exclude https://e-hentai.org/archive*
  15. // @exclude https://e-hentai.org/gallery*
  16. // @exclude https://exhentai.org/archive*
  17. // @exclude https://exhentai.org/gallery*
  18.  
  19. // @require https://code.jquery.com/jquery-3.1.1.min.js
  20. // @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
  21. // @require https://cdn.jsdelivr.net/npm/datatables.net@1.10.16/js/jquery.dataTables.js
  22. // @require https://greasyfork.org/scripts/27104-filesaver/code/FileSaver.js?version=173518
  23. // @author Resuha
  24. // @grant GM_addStyle
  25. // @grant GM_getValue
  26. // @grant GM_setValue
  27. // @grant GM_getResourceText
  28. // ==/UserScript==
  29.  
  30. //Credit to developer of https://github.com/js-cookie/js-cookie
  31.  
  32. //Ver 4.00 changed "view/read later" layout
  33. //Ver 3.00 added "view/read later" functionality
  34. //Ver 2.00 added redirector and cookie remover
  35.  
  36. var targetWebsite = "";
  37. var theme = "";
  38. var readLaterList = [];
  39. var lastHovered;
  40. var currentPageLink = window.location.href;
  41. var dataTable;
  42.  
  43. var cssTxt = GM_getResourceText("exCSS");
  44. GM_addStyle(cssTxt);
  45. var jqueryui = GM_getResourceText("jqueryui");
  46. GM_addStyle(jqueryui);
  47.  
  48. var extraCSS = document.createElement("style");
  49. extraCSS.textContent = `
  50. .link, .option {
  51. cursor: pointer;
  52. text-decoration: underline;
  53. }
  54.  
  55. .option, #placeholderText{font-size: 125%}
  56.  
  57. .readLaterItem{font-size: 125%}
  58.  
  59. .readLaterItem > .option{font-size: 100%}
  60.  
  61. .divHeader{
  62. font-size: 160%;
  63. font-weight: bold;
  64. }
  65.  
  66. #fixedDiv.dark, #readLaterDiv.dark,#successDiv.dark,#previewDiv.dark,#addDiv.dark{
  67. background-color:rgba(64, 64, 0, 1);
  68. border: 1px solid rgba(255, 255, 0, 1);
  69. }
  70. .link.dark{color:yellow}
  71. .option.dark{color:plum}
  72. #mpvAddon.dark{background-color:rgba(64, 64, 0, 0.4)}
  73.  
  74. #fixedDiv.light, #readLaterDiv.light,#successDiv.light,#previewDiv.light,#addDiv.light{
  75. background-color:rgba(255, 255, 0, 1);
  76. border: 1px solid rgba(64, 64, 0, 1);
  77. }
  78. .link.light{color:blue}
  79. .option.light{color:Red}
  80. #mpvAddon.light{background-color:rgba(255, 255, 0, 0.4)}
  81.  
  82. #successDiv{
  83. font-size: 125%;
  84. position: absolute;
  85. transform: translate(-100%, -50%);
  86. z-index:10;
  87. padding: 8px 8px;
  88. }
  89.  
  90. #linker{font-size: 160%}
  91.  
  92. #fixedDiv{
  93. text-align: left !important;
  94. position: fixed;
  95. top: 12px;
  96. right: 12px;
  97. align: left;
  98. padding: 8px 8px;
  99. z-index:10;
  100. }
  101.  
  102. #fixedDivPeek{
  103. text-align: left !important;
  104. position: fixed;
  105. top: 0px;
  106. right: 0px;
  107. width: 10px;
  108. height: 10px;
  109. background-color:rgba(255, 255, 0, 1);
  110. align: left;
  111. padding: 8px 8px;
  112. z-index:10;
  113. }
  114.  
  115. #readLaterDiv{
  116. text-align: left !important;
  117. position:fixed;
  118. bottom: 4%;left: 2%;
  119. align: left;
  120. z-index:10;
  121. padding: 8px 8px;
  122. }
  123.  
  124. #i1 > #mpvAddon{
  125. float:right;
  126. padding:0px 10px 10px 10px;
  127. cursor: pointer;
  128. }
  129.  
  130. #optionDivUL, #readLaterOptionDivUL{
  131. margin: 0;
  132. padding: 0px 0px 0px 20px;
  133. }
  134.  
  135. .topRightDivButton{
  136. float:right;
  137. cursor: pointer;
  138. padding: 0px 4px 2px 3px;
  139. border: 1px solid;
  140. font-size: 80%;
  141. }
  142.  
  143. #previewDiv{
  144. position:fixed;
  145. z-index:10;
  146. padding:4px;
  147. }
  148.  
  149. #previewDiv.showLeft{transform: translate(-110%, -105%)}
  150.  
  151. #previewDiv.showRight{transform: translate(10%, -105%)}
  152.  
  153. #addDiv{
  154. position:absolute;
  155. z-index:10;
  156. padding:4px;
  157. font-size:125%;
  158. cursor: pointer;
  159. }
  160.  
  161. .readLaterItem.hoverPopup.odd.light{background:rgba(240, 240, 0, 1)}
  162. .readLaterItem.hoverPopup.odd.dark{background:rgba(48, 48, 0, 1)}
  163.  
  164. #readLaterTable.light{border:2px rgba(64, 64, 0, 1) solid}
  165. #readLaterTable.dark{border:2px rgba(255, 255, 0, 1) solid}
  166.  
  167. .highlighted{font-weight: bold !important;}
  168. .highlighted.light{color: red}
  169. .highlighted.dark{color: lightblue}
  170. `;
  171. document.head.appendChild(extraCSS);
  172.  
  173. var fixedDiv = `'
  174. <div id="fixedDiv">
  175. <div class="divHeader"><label id="scriptTitleHeader">E(X)Hentai Helper v`+ GM_info.script.version + `</label><label class="topRightDivButton" id="minmaxFixedDivButton">–</label></div><BR>
  176. <div id="linkerDiv">
  177. <label class="divHeader">Linker:</label>
  178. </div>
  179. <div id="readLaterOptionDiv"><BR>
  180. <label class="divHeader">Read Later:</label><BR><ul id="readLaterOptionDivUL"></ul>
  181. </div>
  182. <div id="optionDiv"><BR>
  183. <label class="divHeader">Option:</label><BR><ul id="optionDivUL"></ul>
  184. </div>
  185. </div>'`;
  186. $('body').append(fixedDiv);
  187. fixedDiv = $('#fixedDiv');
  188. linkerDiv = $('#linkerDiv');
  189. optionDiv = $('#optionDiv');
  190. optionDiv.hide();
  191. document.getElementById('minmaxFixedDivButton').addEventListener("click", function () {
  192. if ($('#scriptTitleHeader').is(":visible")) {
  193. hideFixedDivContent();
  194. } else {
  195. showFixedDivContent();
  196. }
  197. });
  198.  
  199. var readLaterDiv = `'
  200. <div id="readLaterDiv" style="width:96%">
  201. <div class="divHeader"><label id="rlDivHeader">Read Later List:</label><label class="topRightDivButton" id="closeReadLaterButton">✖</label></div>
  202. <div id="readLaterTableWrapper"></div>
  203. </div>'`;
  204. $('body').append(readLaterDiv);
  205. document.getElementById('closeReadLaterButton').addEventListener("click", function () {
  206. hide_rlList();
  207. });
  208. readLaterDiv = $('#readLaterDiv');
  209. readLaterDiv.hide();
  210.  
  211. var previewDiv = `'
  212. <div id="previewDiv">
  213. <img id="previewImg" src=""></img><label id="placeholderText"></label>
  214. </div>'`;
  215. $('body').append(previewDiv);
  216. document.getElementById('closeReadLaterButton').addEventListener("click", function () {
  217. hide_rlList();
  218. });
  219. previewDiv = $('#previewDiv');
  220. previewDiv.hide();
  221.  
  222. var addDiv = '<div id="addDiv"><label id="addDivLabel">Add to "view later" list</label></div>';
  223. $('body').append(addDiv);
  224. addDiv = $('#addDiv');
  225. addDiv.hide();
  226. $("#addDiv").click(function () {
  227. var title = lastHovered.getElementsByClassName("id2")[0].childNodes[0].innerHTML;
  228. var link = parse_gallery_identifier(lastHovered.getElementsByClassName("id2")[0].childNodes[0].href);
  229. var thumbnailLink = lastHovered.getElementsByClassName("id3")[0].childNodes[0].childNodes[0].src.replace("exhentai.org", "ehgt.org");
  230. if (thumbnailLink.indexOf("blank.gif") > -1) { //if there is no thumbnail, do AJAX call
  231. $(document).ajaxComplete(function (event, xhr, settings) {
  232. var data = xhr.responseText;
  233. var divStyle = $("#gd1", data).children('div').attr("style").split(" ");
  234. if (divStyle[3].startsWith("url")) {
  235. thumbnailLink = divStyle[3].substring(divStyle[3].indexOf("(") + 1, divStyle[3].indexOf(")"));
  236. } else {
  237. for (i = 0; i < stuff.length; i++) {
  238. if (divStyle[i].startsWith("url")) {
  239. thumbnailLink = divStyle[i].substring(divStyle[i].indexOf("(") + 1, divStyle[i].indexOf(")"));
  240. break;
  241. }
  242. }
  243. }
  244. thumbnailLink = thumbnailLink.replace("exhentai.org", "ehgt.org");
  245. add_rlEntry(title, link, thumbnailLink);
  246. populate_rlDiv();
  247. });
  248. $.ajax(link);
  249. } else {
  250. add_rlEntry(title, link, thumbnailLink);
  251. populate_rlDiv();
  252. }
  253. });
  254.  
  255. if (document.location.href.indexOf('exhentai') !== - 1) {
  256. link = 'https://e-hentai.org' + parse_gallery_identifier();
  257. targetWebsite = 'E-Hentai';
  258. theme = "dark";
  259. } else {
  260. link = 'https://exhentai.org' + parse_gallery_identifier();
  261. targetWebsite = 'ExHentai';
  262. theme = "light";
  263. } // Determine if the current page is E-Hentai or ExHentai
  264.  
  265. readLaterDiv.resizable({
  266. handles: "n,e,ne",
  267. stop: function (event, ui) {
  268. $('th')[0].click();
  269. }
  270. });
  271.  
  272. fixedDiv.addClass("light");
  273. readLaterDiv.addClass("light");
  274. previewDiv.addClass("light");
  275. addDiv.addClass("light");
  276. $('#readLaterTable').addClass("light");
  277. redoTheme();
  278. window.addEventListener('focus', populate_rlDiv);
  279.  
  280. $(document).ready(function () {
  281. populate_rlDiv();
  282. if (document.title == 'Gallery Not Available - E-Hentai Galleries') { // Gallery is expunged in e-hentai
  283. document.location.href = 'https://exhentai.org' + parse_gallery_identifier();
  284. } else if (document.title == "exhentai.org (260×260)") { // Got sadpanda
  285. var bYes = '<button id="yesButton" class="askButton">Yes';
  286. var bNo = '<button id="noButton" class="askButton">No';
  287. var askConfirm = '<div id="confirmDiv">Are you currently logged in at e-hentai.org forum?<label id="message"><br>' + bYes + bNo + '</div>';
  288. $('body').append(askConfirm);
  289. askConfirm = $('#confirmDiv');
  290. askConfirm.css({
  291. 'align': 'center',
  292. 'color': 'blue',
  293. });
  294. $('#yesButton').click(function () { // Clear cookie and refresh
  295. $('#confirmDiv').text("This page will refresh. If you still see this page after the refresh, it is possible that your account is not old enough for exhentai");
  296. setTimeout(function () {
  297. // delete cookie
  298. Cookies.remove('yay', { domain: '.exhentai.org' });
  299. location.reload();
  300. }, 1000);
  301. });
  302. $('#noButton').click(function () { // Redirect to forum
  303. $('#confirmDiv').text("Redirecting to E-Hentai login page");
  304. setTimeout(function () {
  305. document.location.href = 'https://forums.e-hentai.org/index.php?act=Login&CODE=00';
  306. }, 1000);
  307. });
  308. } else {
  309. if (currentPageLink.indexOf("/mpv/") === -1) {
  310. $(".id1").hover(function () {
  311. lastHovered = this;
  312. var location = getOffset(this.getElementsByClassName("id3")[0]);
  313. addDiv.show();
  314. addDiv.css({
  315. "top": location.top + "px",
  316. "left": location.left + "px",
  317. });
  318. if (getIndex_rlList(parse_gallery_identifier(lastHovered.getElementsByClassName("id3")[0].childNodes[0].href)).exactMatch) {
  319. $("#addDivLabel").text('Already added to list');
  320. }
  321. }, function () {
  322. addDiv.hide();
  323. $("#addDivLabel").text('Add to "view later" list');
  324. });
  325.  
  326. addDiv.hover(function () {
  327. addDiv.show();
  328. if (getIndex_rlList(parse_gallery_identifier(lastHovered.getElementsByClassName("id3")[0].childNodes[0].href)).exactMatch) {
  329. $("#addDivLabel").text('Already added to list');
  330. } else {
  331. $("#addDiv").css("color", "red");
  332. }
  333. }, function () {
  334. $("#addDiv").hide();
  335. $("#addDiv").css("color", "");
  336. });
  337.  
  338. var link;
  339. var linker;
  340.  
  341. if (targetWebsite == 'ExHentai') {
  342. if (document.location.href == 'https://upload.e-hentai.org/manage.php') {
  343. linker = "https://exhentai.org/upload/manage.php";
  344. } else {
  345. linker = "https://exhentai.org" + parse_gallery_identifier();
  346. }
  347. linkerDiv.append(createLink("linker", linker, "To ExHentai"));
  348. } else {
  349. if (document.location.href == 'https://exhentai.org/upload/manage.php') {
  350. linker = "https://upload.e-hentai.org/manage.php";
  351. } else {
  352. linker = "https://e-hentai.org" + parse_gallery_identifier();
  353. }
  354. linkerDiv.append(createLink("linker", linker, "To E-Hentai"));
  355. } // Add option to switch between E-Hentai and ExHentai in the top bar of the page
  356.  
  357. document.getElementById('linker').addEventListener("click", function () {
  358. document.location.href = linker;
  359. });
  360.  
  361. // Setting page
  362. if (currentPageLink.indexOf("uconfig") > -1) {
  363. optionDiv.show();
  364. var optionArray = [];
  365. optionArray.push(createOptionWithClass("loadSettingOption", "Load saved user setting", "setting"));
  366. optionArray.push(createOptionWithClass("saveSettingOption", "Save current user setting", "setting"));
  367.  
  368. for (i = 0; i < optionArray.length; i++) {
  369. $('#optionDivUL').append(optionArray[i]);
  370. }
  371.  
  372. document.getElementById('loadSettingOption').addEventListener("click", function () {
  373. var setting = GM_getValue("userSetting", "");
  374. if (setting !== "") {
  375. if (document.location.href.indexOf('exhentai') !== - 1) {
  376. Cookies.set("uconfig", setting, { domain: ".exhentai.org", expires: 365 });
  377. } else {
  378. Cookies.set("uconfig", setting, { domain: ".e-hentai.org", expires: 365 });
  379. }
  380. alert("User setting loaded");
  381. location.reload();
  382. } else {
  383. alert("Nothing to load");
  384. }
  385. });
  386. document.getElementById('saveSettingOption').addEventListener("click", function () {
  387. GM_setValue("userSetting", Cookies.get("uconfig"));
  388. // Maybe have an option to save it to file
  389. alert("User setting saved");
  390. });
  391. }
  392.  
  393. // Gallery page
  394. if (currentPageLink.indexOf("/g/") > -1 || currentPageLink.indexOf("/s/") > -1) {
  395. var indexMatch = getIndex_rlList(parse_gallery_identifier());
  396. if (indexMatch.exactMatch === true && readLaterList[indexMatch.index].thumbnailLink !== "undefined") {
  397. $('#readLaterOptionDivUL').append(createOption("addReadLaterOption", 'Already in "view later" list'));
  398. $('#addReadLaterOption').css({
  399. 'text-decoration': 'none',
  400. 'cursor': 'default',
  401. });
  402. } else {
  403. $('#readLaterOptionDivUL').append(createOption("addReadLaterOption", 'Add to "view later" list'));
  404. document.getElementById('addReadLaterOption').addEventListener("click", add_rlEntryFunction);
  405. }
  406. if (indexMatch.index > -1) {
  407. $('#readLaterOptionDivUL').append(createOption("removeReadLaterOption", 'Remove from "view later" list'));
  408. document.getElementById('removeReadLaterOption').addEventListener("click", remove_rlEntryFunction);
  409. }
  410.  
  411. if (currentPageLink.indexOf("/s/") > -1) {
  412. var successDiv;
  413. if (targetWebsite === "E-Hentai") {
  414. $('#i1').prepend('<div id="mpvAddon" class="dark"></div>');
  415. $('#mpvAddon').append('<img id="mpvSave_rl" src="http://i.imgur.com/rdXO8o2.png" title="Save this page to view later list (E<->Ex extension)" style="margin-top:10px; opacity:0.8">');
  416. $('#mpvAddon').append('<img id="mpvView_rl" src="http://i.imgur.com/ywCl5NP.png" title="Open view later list (E<->Ex extension)" style="margin-top:5px; opacity:0.8">');
  417. successDiv = '<div id="successDiv" class="dark">Added/updated "view later" list</div>';
  418. } else {
  419. $('#i1').prepend('<div id="mpvAddon" class="light"></div>');
  420. $('#mpvAddon').append('<img id="mpvSave_rl" src="http://i.imgur.com/tWBUjde.png" title="Save this page to view later list (E<->Ex extension)" style="margin-top:10px; opacity:0.8">');
  421. $('#mpvAddon').append('<img id="mpvView_rl" src="http://i.imgur.com/XH8qCCi.png" title="Open view later list (E<->Ex extension)" style="margin-top:5px; opacity:0.8">');
  422. successDiv = '<div id="successDiv" class="light">Added/updated "view later" list</div>';
  423. }
  424. $('body').append(successDiv);
  425. successDiv = $('#successDiv');
  426. successDiv.hide();
  427.  
  428. document.getElementById('mpvSave_rl').addEventListener("click", add_rlEntryFunction);
  429. document.getElementById('mpvView_rl').addEventListener("click", view_rlButtonClick);
  430. hideFixedDivContent();
  431.  
  432. var oldLocation = location.href;
  433. setInterval(function () {
  434. if (location.href !== oldLocation) {
  435. oldLocation = location.href;
  436. $('#addReadLaterOption').text('Add to "view later" list');
  437. document.getElementById('addReadLaterOption').addEventListener("click", add_rlEntryFunction);
  438. $('#addReadLaterOption').css({
  439. 'text-decoration': 'underline',
  440. 'cursor': 'pointer',
  441. });
  442. }
  443. }, 2000); // check 2 seconds
  444. }
  445. }
  446.  
  447. $('#readLaterOptionDivUL').append(createOption("seeReadLaterListOption", 'Show "view later" list'));
  448. document.getElementById('seeReadLaterListOption').addEventListener("click", view_rlButtonClick);
  449.  
  450. // If there is no option available, hide the optionDiv completely
  451. if ($('.setting').length === 0) {
  452. optionDiv.hide();
  453. }
  454.  
  455. $('#readLaterOptionDivUL').append(createOption("export_rlListOption", 'Export list'));
  456. document.getElementById('export_rlListOption').addEventListener("click", exportFunction);
  457.  
  458. $('#readLaterOptionDivUL').append(createOption("import_rlListOption", 'Import list'));
  459. document.getElementById('import_rlListOption').addEventListener("click", importFunction);
  460.  
  461. $('#readLaterOptionDivUL').append(createOption("reset_rlListOption", 'Empty the list'));
  462. document.getElementById('reset_rlListOption').addEventListener("click", resetList);
  463. } else {
  464. var successDiv;
  465. if (targetWebsite === "E-Hentai") {
  466. $('#bar3').append('<div id="mpvAddon" class="dark"></div>');
  467. $('#mpvAddon').append('<img id="mpvSave_rl" src="http://i.imgur.com/rdXO8o2.png" title="Save this page to view later list (E<->Ex extension)" style="margin-top:10px; opacity:0.8">');
  468. $('#mpvAddon').append('<img id="mpvView_rl" src="http://i.imgur.com/ywCl5NP.png" title="Open view later list (E<->Ex extension)" style="margin-top:5px; opacity:0.8">');
  469. successDiv = '<div id="successDiv" class="dark">Added/updated "view later" list</div>';
  470. } else {
  471. $('#bar3').append('<div id="mpvAddon" class="light"></div>');
  472. $('#mpvAddon').append('<img id="mpvSave_rl" src="http://i.imgur.com/tWBUjde.png" title="Save this page to view later list (E<->Ex extension)" style="margin-top:10px; opacity:0.8">');
  473. $('#mpvAddon').append('<img id="mpvView_rl" src="http://i.imgur.com/XH8qCCi.png" title="Open view later list (E<->Ex extension)" style="margin-top:5px; opacity:0.8">');
  474. successDiv = '<div id="successDiv" class="light">Added/updated "view later" list</div>';
  475. }
  476. $('body').append(successDiv);
  477. successDiv = $('#successDiv');
  478. successDiv.hide();
  479. document.getElementById('mpvSave_rl').addEventListener("click", add_rlButtonClick);
  480. document.getElementById('mpvView_rl').addEventListener("click", view_rlButtonClick);
  481. $('#fixedDiv').hide();
  482. }
  483. redoTheme();
  484. }
  485. });
  486.  
  487. function resetList() {
  488. if (confirm("Are you sure you want to empty the list? You cannot undo this unless you export it first.")) {
  489. GM_setValue("readLater", "");
  490. }
  491. }
  492.  
  493. function add_rlButtonClick() { //for MPV
  494. var pos = getOffset(this);
  495. $('#successDiv').css({
  496. 'top': pos.top + 12,
  497. 'left': pos.left - 12,
  498. });
  499. var currentPageNum = 0;
  500. var currentScrollLocation = $('#pane_images').scrollTop();
  501. var allImgDiv = $('#pane_images_inner').children('div');
  502. for (i = 0; i < allImgDiv.length; i++) {
  503. currentScrollLocation -= allImgDiv[i].offsetHeight;
  504. currentPageNum++;
  505. if (currentScrollLocation <= 0) {
  506. break;
  507. }
  508. }
  509. var lastPage = document.getElementById("pane_thumbs_inner").childElementCount;
  510. var title = document.title.substring(0, document.title.lastIndexOf(" - ")) + " {" + currentPageNum + "/" + lastPage + "}";
  511. var link = parse_gallery_identifier();
  512. link = link.substring(0, link.lastIndexOf("/")) + "/#page" + currentPageNum;
  513.  
  514. var mainPage_URL = window.location.href.substring(0, window.location.href.lastIndexOf("/")).replace("mpv", "g");
  515. var thumbnailLink;
  516.  
  517. $(document).ajaxComplete(function (event, xhr, settings) {
  518. var data = xhr.responseText;
  519. var divStyle = $("#gd1", data).children('div').attr("style").split(" ");
  520. if (divStyle[3].startsWith("url")) {
  521. thumbnailLink = divStyle[3].substring(divStyle[3].indexOf("(") + 1, divStyle[3].indexOf(")"));
  522. } else {
  523. for (i = 0; i < stuff.length; i++) {
  524. if (divStyle[i].startsWith("url")) {
  525. thumbnailLink = divStyle[i].substring(divStyle[i].indexOf("(") + 1, divStyle[i].indexOf(")"));
  526. break;
  527. }
  528. }
  529. }
  530. thumbnailLink = thumbnailLink.replace("exhentai.org", "ehgt.org");
  531. add_rlEntry(title, link, thumbnailLink);
  532. populate_rlDiv();
  533. $('#successDiv').fadeIn();
  534. setTimeout(function () {
  535. $('#successDiv').fadeOut();
  536. }, 2000);
  537. });
  538. $.ajax(mainPage_URL);
  539. }
  540.  
  541. function exportFunction() {
  542. var str = rlArrayToString(readLaterList);
  543. var filename = "eh_view_later_list";
  544. var blob = new Blob([str], { type: "text/plain;charset=utf-8" });
  545. saveAs(blob, filename + ".txt");
  546. }
  547.  
  548. function importFunction() {
  549. var input = document.createElement('input');
  550. $(input).attr("type", "file");
  551. input.addEventListener("change", function readSingleFile(e) {
  552. var file = e.target.files[0];
  553. if (!file) {
  554. return;
  555. }
  556. var reader = new FileReader();
  557. reader.onload = function (e) {
  558. var contents = e.target.result;
  559. GM_setValue("readLater", contents);
  560. populate_rlDiv();
  561. var successDiv;
  562. successDiv = '<div id="successDiv" class="light">List successfully loaded</div>';
  563. $('body').append(successDiv);
  564. successDiv = $('#successDiv');
  565. successDiv.hide();
  566. $('#successDiv').css({
  567. 'top': 1 + 12,
  568. 'left': 1 - 12,
  569. });
  570. $('#successDiv').fadeIn();
  571. setTimeout(function () {
  572. $('#successDiv').fadeOut();
  573. }, 2000);
  574. };
  575. reader.readAsText(file);
  576. }, false);
  577. $(input).trigger('click');
  578. }
  579.  
  580. function view_rlButtonClick() {
  581. if ($('#readLaterDiv').is(":visible")) {
  582. hide_rlList();
  583. } else {
  584. show_rlList();
  585. populate_rlDiv();
  586. }
  587. }
  588.  
  589. function getGalleryID(pageHref) {
  590. var splitHref = pageHref.split("/");
  591. if (pageHref.indexOf("/g/") > -1 || pageHref.indexOf("/mpv/") > -1) {
  592. return splitHref[2];
  593. } else {
  594. return splitHref[3].substring(0, splitHref[3].indexOf("-"));
  595. }
  596. }
  597.  
  598. function populate_rlDiv() {
  599. readLaterList = load_rlList();
  600. console.log($('#readLaterDiv').is(':visible'));
  601. console.log($('.readLaterItem').length, readLaterList.length);
  602. if ($('#readLaterDiv').is(':visible') && ($('.readLaterItem').length === 0 || $('.readLaterItem').length !== readLaterList.length)) {
  603. $("#readLaterTable_wrapper").remove(); // remove existing ones and repopulate the list
  604. $('#readLaterTableWrapper').append(
  605. `<table id="readLaterTable" class="display" cellspacing="0" width="100%">
  606. <thead>
  607. <tr>
  608. <th>Delete</th>
  609. <th>EH Link</th>
  610. <th>EX Link</th>
  611. <th>Title</th>
  612. <th>Circle</th>
  613. <th>Artist</th>
  614. <th>Event</th>
  615. </tr>
  616. </thead>
  617. <tbody id="readLaterTableBody">
  618. </tbody>
  619. </table>`
  620. );
  621. $("#rlDivHeader").text("Read Later List: (" + readLaterList.length + " items)");
  622. for (i = 0; i < readLaterList.length; i++) {
  623. var entryDOM = '<tr class="readLaterItem hoverPopup light" id="item' + i + '">';
  624. if (readLaterList[i] !== null) {
  625. entryDOM += '<td class="readLaterRemoveItem option light">Remove</td>';
  626. entryDOM += '<td>' + createLink("", "https://e-hentai.org" + readLaterList[i].link, "Read") + '</td>';
  627. entryDOM += '<td>' + createLink("", "https://exhentai.org" + readLaterList[i].link, "Read") + '</td>';
  628. entryDOM += '<td>' + readLaterList[i].title + '</td>';
  629. entryDOM += '<td>' + readLaterList[i].group + '</td>';
  630. entryDOM += '<td>' + readLaterList[i].artist + '</td>';
  631. entryDOM += '<td>' + readLaterList[i].eventName + '</td>';
  632. }
  633. entryDOM += "</tr>"
  634. $('#readLaterTableBody').append(entryDOM);
  635. }
  636. $(".hoverPopup").mousemove(function (event) { //on
  637. previewDiv.css({
  638. "left": event.clientX + "px",
  639. "top": event.clientY + "px",
  640. });
  641. if (event.clientX > (document.getElementById('readLaterDiv').offsetWidth * 0.75)) {
  642. previewDiv.addClass("showLeft");
  643. previewDiv.removeClass("showRight");
  644. } else {
  645. previewDiv.addClass("showRight");
  646. previewDiv.removeClass("showLeft");
  647. }
  648. previewDiv.show();
  649. var arrayNumber = $(this).attr('id').replace("item", "");
  650. var thumbLink = readLaterList[arrayNumber].thumbnailLink;
  651. if (thumbLink !== undefined && thumbLink !== "" && thumbLink !== "undefined") {
  652. $("#previewImg").attr("src", thumbLink);
  653. } else {
  654. $("#previewImg").attr("src", "");
  655. $("#placeholderText").text("Sorry, please re-add the gallery to see thumbnail");
  656. previewDiv.css("width", "160px");
  657. }
  658. $(this).addClass('highlighted');
  659. });
  660. $(".hoverPopup").mouseleave(function (event) { //off
  661. previewDiv.hide();
  662. $("#placeholderText").text("");
  663. previewDiv.css("width", "");
  664. $(this).removeClass('highlighted');
  665. });
  666. var removeItem = document.getElementsByClassName("readLaterRemoveItem");
  667. for (i = 0; i < removeItem.length; i++) {
  668. removeItem[i].addEventListener("click", function () {
  669. //this.parentNode.remove(this);
  670. remove_rlEntry(parse_gallery_identifier(this.nextElementSibling.children[0].href));
  671. });
  672. }
  673. redoTheme();
  674. dataTable = $('#readLaterTable').DataTable({
  675. "scrollY": "400px",
  676. "scrollCollapse": true,
  677. "paging": false,
  678. "responsive": true,
  679. "bAutoWidth": true,
  680. });
  681. // setTimeout(function () {
  682. // $('th')[0].click();
  683. // }, 100)
  684. }
  685. }
  686.  
  687. function remove_rlEntryFunction(event) {
  688. remove_rlEntry(parse_gallery_identifier());
  689. $(this).parent().remove();
  690. }
  691.  
  692. function add_rlEntryFunction() {
  693. var title;
  694. if (window.location.href.indexOf("/g/") > -1) {
  695. title = $('#gn').text();
  696. } else {
  697. title = $('#i1').children('h1').text() + " {" + $('#i2').children('.sn').children('div').text().replace(/ /g, "") + "}";
  698. }
  699. var thumbnailLink;
  700.  
  701. if (currentPageLink.indexOf("/s/") > -1) {
  702. var mainPage_URL = $('#i5').children('.sb').children('a').attr('href');
  703. $(document).ajaxComplete(function (event, xhr, settings) {
  704. var data = xhr.responseText;
  705. var divStyle = $("#gd1", data).children('div').attr("style").split(" ");
  706. if (divStyle[3].startsWith("url")) {
  707. thumbnailLink = divStyle[3].substring(divStyle[3].indexOf("(") + 1, divStyle[3].indexOf(")"));
  708. } else {
  709. for (i = 0; i < stuff.length; i++) {
  710. if (divStyle[i].startsWith("url")) {
  711. thumbnailLink = divStyle[i].substring(divStyle[i].indexOf("(") + 1, divStyle[i].indexOf(")"));
  712. break;
  713. }
  714. }
  715. }
  716. thumbnailLink = thumbnailLink.replace("exhentai.org", "ehgt.org");
  717. add_rlEntry(title, parse_gallery_identifier(), thumbnailLink);
  718. populate_rlDiv();
  719. $('#successDiv').fadeIn();
  720. setTimeout(function () {
  721. $('#successDiv').fadeOut();
  722. }, 2000);
  723. $('#addReadLaterOption').text('Added to list');
  724. $('#addReadLaterOption').off();
  725. $('#addReadLaterOption').css({
  726. 'text-decoration': 'none',
  727. 'cursor': 'default',
  728. });
  729. });
  730. $.ajax(mainPage_URL);
  731. } else {
  732. var divStyle = $("#gd1").children('div').attr("style").split(" ");
  733. if (divStyle[3].startsWith("url")) {
  734. thumbnailLink = divStyle[3].substring(divStyle[3].indexOf("(") + 1, divStyle[3].indexOf(")"));
  735. } else {
  736. for (i = 0; i < stuff.length; i++) {
  737. if (divStyle[i].startsWith("url")) {
  738. thumbnailLink = divStyle[i].substring(divStyle[i].indexOf("(") + 1, divStyle[i].indexOf(")"));
  739. break;
  740. }
  741. }
  742. }
  743. thumbnailLink = thumbnailLink.replace("exhentai.org", "ehgt.org");
  744. add_rlEntry(title, parse_gallery_identifier(), thumbnailLink);
  745. populate_rlDiv();
  746. $('#addReadLaterOption').text('Added to list');
  747. $('#addReadLaterOption').off();
  748. $('#addReadLaterOption').css({
  749. 'text-decoration': 'none',
  750. 'cursor': 'default',
  751. });
  752. }
  753. }
  754.  
  755. function show_rlList() {
  756. readLaterDiv.show();
  757. $('#seeReadLaterListOption').text('Hide "view later" list');
  758. }
  759.  
  760. function hide_rlList() {
  761. readLaterDiv.hide();
  762. $('#seeReadLaterListOption').text('Show "view later" list');
  763. }
  764.  
  765. function createLink(id, link, text) {
  766. return '<a id="' + id + '" href="' + link + '" class="link light">' + text + '</a>';
  767. }
  768.  
  769. function createOption(id, text) {
  770. return '<li><label id="' + id + '" class="option light">' + text + '</label></li>';
  771. }
  772.  
  773. function createOptionWithClass(id, text, extraClass) {
  774. return '<li><label id="' + id + '" class="option light ' + extraClass + '">' + text + '</label></li>';
  775. }
  776.  
  777. function rlStringToArray(string) {
  778. var returnArray = [];
  779. var splitString = string.split(";");
  780. for (i = 0; i < splitString.length; i++) {
  781. var entrySplit = splitString[i].split("::");
  782. var entry = create_rlEntry(entrySplit[0], entrySplit[1], entrySplit[2]);
  783. if (entry !== null) {
  784. returnArray.push(entry);
  785. }
  786. }
  787. return returnArray;
  788. }
  789.  
  790. function rlArrayToString(array) {
  791. var returnString = "";
  792. for (i = 0; i < array.length; i++) {
  793. returnString += getStringEntry(array[i]);
  794. if (i !== (array.length - 1)) {
  795. returnString += ";";
  796. }
  797. }
  798. return returnString;
  799. }
  800.  
  801. function create_rlEntry(title, link, thumbnailLink) {
  802. if (!link || !title || !thumbnailLink) {
  803. return null;
  804. } else {
  805. titleParse = parseTitle(title);
  806. return { eventName: titleParse.eventName, group: titleParse.group, artist: titleParse.artist, title: titleParse.title, link: link, thumbnailLink: thumbnailLink };
  807. }
  808. }
  809.  
  810. function getStringEntry(entry) {
  811. var returnString = unparseTitle(entry)
  812. returnString += "::" + entry.link;
  813. returnString += "::" + entry.thumbnailLink;
  814. return returnString;
  815. }
  816.  
  817. function load_rlList() {
  818. var rlString = GM_getValue("readLater", "");
  819. return rlStringToArray(rlString);
  820. }
  821.  
  822. function add_rlEntry(title, link, thumbnailLink) {
  823. titleParsed = parseTitle(title);
  824. if (getIndex_rlList(link).index === -1) {
  825. var entry = create_rlEntry(title, link, thumbnailLink);
  826. var entryString = title + "::" + link + "::" + thumbnailLink;
  827. // add to current saved list
  828. readLaterList.push(entry);
  829. // add to GM_saved list
  830. var newValue = GM_getValue("readLater", "");
  831. if (newValue !== "") {
  832. newValue += ";";
  833. }
  834. newValue += entryString;
  835. GM_setValue("readLater", newValue);
  836. } else {
  837. var replaceItemIndex = getIndex_rlList(link).index;
  838. // update current saved list
  839. readLaterList[replaceItemIndex].eventName = titleParsed.eventName;
  840. readLaterList[replaceItemIndex].group = titleParsed.group;
  841. readLaterList[replaceItemIndex].artist = titleParsed.artist;
  842. readLaterList[replaceItemIndex].title = titleParsed.title;
  843. readLaterList[replaceItemIndex].link = link;
  844. readLaterList[replaceItemIndex].thumbnailLink = thumbnailLink;
  845. // update current saved list
  846. var savedValue = GM_getValue("readLater", "");
  847. var array = rlStringToArray(savedValue);
  848. array[replaceItemIndex].eventName = titleParsed.eventName;
  849. array[replaceItemIndex].group = titleParsed.group;
  850. array[replaceItemIndex].artist = titleParsed.artist;
  851. array[replaceItemIndex].title = titleParsed.title;
  852. array[replaceItemIndex].link = link;
  853. array[replaceItemIndex].thumbnailLink = thumbnailLink;
  854. savedValue = rlArrayToString(array);
  855. GM_setValue("readLater", savedValue);
  856. }
  857. }
  858.  
  859. function remove_rlEntry(link) {
  860. var removeItemIndex = getIndex_rlList(link).index;
  861. // remove from current saved list
  862. readLaterList.splice(removeItemIndex, 1);
  863.  
  864. // remove from GM_saved list
  865. var savedValue = GM_getValue("readLater", "");
  866. var array = rlStringToArray(savedValue);
  867. array.splice(removeItemIndex, 1);
  868. savedValue = rlArrayToString(array);
  869. GM_setValue("readLater", savedValue);
  870.  
  871. if (location.href.indexOf(link) > -1) {
  872. $('#addReadLaterOption').text('Add to "view later" list');
  873. if (currentPageLink.indexOf("/mpv/") === -1) {
  874. document.getElementById('addReadLaterOption').addEventListener("click", add_rlEntryFunction);
  875. }
  876. $('#addReadLaterOption').css({
  877. 'text-decoration': 'underline',
  878. 'cursor': 'pointer',
  879. });
  880. }
  881.  
  882. if (document.getElementsByClassName("readLaterItem").length === 0) {
  883. hide_rlList();
  884. }
  885. populate_rlDiv();
  886. }
  887.  
  888. function getIndex_rlList(link) {
  889. var galleryID = getGalleryID(link);
  890. for (i = 0; i < readLaterList.length; i++) {
  891. if (readLaterList[i].link !== undefined && readLaterList[i].link.indexOf(galleryID) > -1) {
  892. if (readLaterList[i].link === link) {
  893. return { index: i, exactMatch: true };
  894. } else {
  895. return { index: i, exactMatch: false };
  896. }
  897. }
  898. }
  899. return { index: -1, exactMatch: false };
  900. }
  901.  
  902. // Parse the gallery link
  903. function parse_gallery_identifier(link) {
  904. var identifier_start;
  905. var identifier_end;
  906. var identifier;
  907. if (link === undefined) {
  908. if (location.href.indexOf('e-hentai.org') !== - 1) {
  909. identifier_start = location.href.indexOf('e-hentai.org') + 12;
  910. } else if (location.href.indexOf('exhentai.org') !== - 1) {
  911. identifier_start = location.href.indexOf('exhentai.org') + 12;
  912. }
  913. identifier_end = location.href.length;
  914. identifier = location.href.substr(identifier_start, identifier_end);
  915. } else {
  916. if (link.indexOf('e-hentai.org') !== - 1) {
  917. identifier_start = link.indexOf('e-hentai.org') + 12;
  918. } else if (link.indexOf('exhentai.org') !== - 1) {
  919. identifier_start = link.indexOf('exhentai.org') + 12;
  920. }
  921. identifier_end = link.length;
  922. identifier = link.substr(identifier_start, identifier_end);
  923. }
  924. return identifier;
  925. }
  926.  
  927. ////////////////////////////////////////////////////////////////
  928. // Parsing and unparsing title
  929. ////////////////////////////////////////////////////////////////
  930.  
  931. function unparseTitle(parsedTitle) {
  932. var returnString = "";
  933. if (parsedTitle.eventName !== "") {
  934. returnString += "(" + parsedTitle.eventName + ") ";
  935. }
  936. if (parsedTitle.group !== "" && parsedTitle.artist !== "") {
  937. returnString += "[" + parsedTitle.group + " (" + parsedTitle.artist + ")] ";
  938. } else if (parsedTitle.artist !== "") {
  939. returnString += "[" + parsedTitle.artist + "] ";
  940. }
  941. returnString += parsedTitle.title;
  942. return returnString;
  943. }
  944.  
  945. function parseTitle(stringTitle) {
  946. var eventName = "";
  947. var group = "";
  948. var artist = "";
  949. var title = "";
  950.  
  951. var title_done = false;
  952. var mode = "";
  953. var temp = "";
  954.  
  955. for (j = 0; j < stringTitle.length; j++) {
  956. if (stringTitle[j] === "(" && mode === "") {
  957. mode = "event";
  958. } else if (stringTitle[j] === ")" && mode === "event") {
  959. mode = "";
  960. } else if (stringTitle[j] === "[" && mode === "") {
  961. mode = "info";
  962. } else if (stringTitle[j] === "(" && mode === "info") {
  963. mode = "artist";
  964. group = temp;
  965. } else if (stringTitle[j] === ")" && mode === "artist") {
  966. mode = "title";
  967. } else if (stringTitle[j] === "]" && mode === "info") {
  968. mode = "title";
  969. artist = temp;
  970. } else if (stringTitle[j] === "]" && stringTitle[j - 1] === ")") {
  971. mode = "title";
  972. } else {
  973. switch (mode) {
  974. case "":
  975. title += stringTitle[j];
  976. break;
  977. case "event":
  978. eventName += stringTitle[j];
  979. break;
  980. case "info":
  981. temp += stringTitle[j];
  982. break;
  983. case "artist":
  984. artist += stringTitle[j];
  985. break;
  986. case "title":
  987. title += stringTitle[j];
  988. break;
  989. }
  990. }
  991. }
  992. return { eventName: eventName.trim(), group: group.trim(), artist: artist.trim(), title: title.trim() };
  993. }
  994.  
  995. ////////////////////////////////////////////////////////////////
  996. // UI Related
  997. ////////////////////////////////////////////////////////////////
  998.  
  999. function hideFixedDivContent() {
  1000. $('#minmaxFixedDivButton').text("+");
  1001. $('#scriptTitleHeader').hide();
  1002. $('#linkerDiv').hide();
  1003. $('#readLaterOptionDiv').hide();
  1004. $('#optionDiv').hide();
  1005. }
  1006.  
  1007. function showFixedDivContent() {
  1008. $('#minmaxFixedDivButton').text("–");
  1009. $('#scriptTitleHeader').show();
  1010. $('#linkerDiv').show();
  1011. $('#readLaterOptionDiv').show();
  1012. if ($('.setting').length > 0) {
  1013. $('#optionDiv').show();
  1014. }
  1015. }
  1016.  
  1017. function redoTheme() {
  1018. if (theme === "dark") {
  1019. $('.light').addClass("dark");
  1020. $('.light').removeClass("light");
  1021. } else {
  1022. $('.dark').addClass("light");
  1023. $('.dark').removeClass("dark");
  1024. }
  1025. }
  1026.  
  1027. ////////////////////////////////////////////////////////////////
  1028. // External codes
  1029. ////////////////////////////////////////////////////////////////
  1030.  
  1031. // Credit to Adam Grant: http://stackoverflow.com/questions/442404/retrieve-the-position-x-y-of-an-html-element
  1032. function getOffset(el) {
  1033. el = el.getBoundingClientRect();
  1034. return {
  1035. left: el.left + window.scrollX,
  1036. top: el.top + window.scrollY
  1037. };
  1038. }
  1039.  
  1040. /*!
  1041. * JavaScript Cookie v2.1.3
  1042. * https://github.com/js-cookie/js-cookie
  1043. *
  1044. * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
  1045. * Released under the MIT license
  1046. */
  1047. ; (function (factory) {
  1048. var registeredInModuleLoader = false;
  1049. if (typeof define === 'function' && define.amd) {
  1050. define(factory);
  1051. registeredInModuleLoader = true;
  1052. }
  1053. if (typeof exports === 'object') {
  1054. module.exports = factory();
  1055. registeredInModuleLoader = true;
  1056. }
  1057. if (!registeredInModuleLoader) {
  1058. var OldCookies = window.Cookies;
  1059. var api = window.Cookies = factory();
  1060. api.noConflict = function () {
  1061. window.Cookies = OldCookies;
  1062. return api;
  1063. };
  1064. }
  1065. }(function () {
  1066. function extend() {
  1067. var i = 0;
  1068. var result = {};
  1069. for (; i < arguments.length; i++) {
  1070. var attributes = arguments[i];
  1071. for (var key in attributes) {
  1072. result[key] = attributes[key];
  1073. }
  1074. }
  1075. return result;
  1076. }
  1077.  
  1078. function init(converter) {
  1079. function api(key, value, attributes) {
  1080. var result;
  1081. if (typeof document === 'undefined') {
  1082. return;
  1083. }
  1084.  
  1085. // Write
  1086.  
  1087. if (arguments.length > 1) {
  1088. attributes = extend({
  1089. path: '/'
  1090. }, api.defaults, attributes);
  1091.  
  1092. if (typeof attributes.expires === 'number') {
  1093. var expires = new Date();
  1094. expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
  1095. attributes.expires = expires;
  1096. }
  1097.  
  1098. try {
  1099. result = JSON.stringify(value);
  1100. if (/^[\{\[]/.test(result)) {
  1101. value = result;
  1102. }
  1103. } catch (e) { }
  1104.  
  1105. if (!converter.write) {
  1106. value = encodeURIComponent(String(value))
  1107. .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
  1108. } else {
  1109. value = converter.write(value, key);
  1110. }
  1111.  
  1112. key = encodeURIComponent(String(key));
  1113. key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
  1114. key = key.replace(/[\(\)]/g, escape);
  1115.  
  1116. return (document.cookie = [
  1117. key, '=', value,
  1118. attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
  1119. attributes.path ? '; path=' + attributes.path : '',
  1120. attributes.domain ? '; domain=' + attributes.domain : '',
  1121. attributes.secure ? '; secure' : ''
  1122. ].join(''));
  1123. }
  1124.  
  1125. // Read
  1126.  
  1127. if (!key) {
  1128. result = {};
  1129. }
  1130.  
  1131. // To prevent the for loop in the first place assign an empty array
  1132. // in case there are no cookies at all. Also prevents odd result when
  1133. // calling "get()"
  1134. var cookies = document.cookie ? document.cookie.split('; ') : [];
  1135. var rdecode = /(%[0-9A-Z]{2})+/g;
  1136. var i = 0;
  1137.  
  1138. for (; i < cookies.length; i++) {
  1139. var parts = cookies[i].split('=');
  1140. var cookie = parts.slice(1).join('=');
  1141.  
  1142. if (cookie.charAt(0) === '"') {
  1143. cookie = cookie.slice(1, -1);
  1144. }
  1145.  
  1146. try {
  1147. var name = parts[0].replace(rdecode, decodeURIComponent);
  1148. cookie = converter.read ?
  1149. converter.read(cookie, name) : converter(cookie, name) ||
  1150. cookie.replace(rdecode, decodeURIComponent);
  1151.  
  1152. if (this.json) {
  1153. try {
  1154. cookie = JSON.parse(cookie);
  1155. } catch (e) { }
  1156. }
  1157.  
  1158. if (key === name) {
  1159. result = cookie;
  1160. break;
  1161. }
  1162.  
  1163. if (!key) {
  1164. result[name] = cookie;
  1165. }
  1166. } catch (e) { }
  1167. }
  1168.  
  1169. return result;
  1170. }
  1171.  
  1172. api.set = api;
  1173. api.get = function (key) {
  1174. return api.call(api, key);
  1175. };
  1176. api.getJSON = function () {
  1177. return api.apply({
  1178. json: true
  1179. }, [].slice.call(arguments));
  1180. };
  1181. api.defaults = {};
  1182.  
  1183. api.remove = function (key, attributes) {
  1184. api(key, '', extend(attributes, {
  1185. expires: -1
  1186. }));
  1187. };
  1188.  
  1189. api.withConverter = init;
  1190.  
  1191. return api;
  1192. }
  1193.  
  1194. return init(function () { });
  1195. }));