Cleanreads

Cleanreads userscript for Goodreads.com

当前为 2019-07-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Cleanreads
  3. // @namespace http://hermanfassett.me
  4. // @version 1.2
  5. // @description Cleanreads userscript for Goodreads.com
  6. // @author Herman Fassett
  7. // @match https://www.goodreads.com/*
  8. // @grant GM_addStyle
  9. // ==/UserScript==
  10.  
  11. GM_addStyle( `
  12. .contentComment { padding: 10px 5px 10px 5px; }
  13. .contentClean { color: green; }
  14. .contentNotClean { color: red; }
  15. .contentUnknown { color: blue; }
  16. #crSettingsDialog {
  17. width: 500px;
  18. height: 500px;
  19. position: fixed;
  20. top: 50%;
  21. left: 50%;
  22. transform: translate(-50%, -50%);
  23. background: white;
  24. border: 1px solid rgba(0,0,0,0.15);
  25. display: none;
  26. }
  27. #crSettingsHeader {
  28. height: 50px;
  29. width: 100%;
  30. background: #F4F1EA;
  31. text-align: center;
  32. box-shadow: 0 1px 2px rgba(0,0,0,0.15);
  33. }
  34. #crSettingsHeader h1 {
  35. line-height: 50px;
  36. color: #382110;
  37. }
  38. #crSettingsHeader h1, .crSettingsHeader {
  39. font-family: "Lato", "Helvetica Neue", "Helvetica", sans-serif;
  40. }
  41. .crSettingsHeader, #crSettingsTermButtons { padding-top: 20px; }
  42. #crSettingsTermButtons button { margin-right: 5px; }
  43. #crSettingsBody { height: 400px; overflow: auto; }
  44. #crSettingsFooter {
  45. height: 50px;
  46. width: 100%;
  47. box-shadow: 1px 0 2px rgba(0,0,0,0.15);
  48. }
  49. #crSettingsFooter button {
  50. float: right;
  51. margin: 10px 10px 0 0;
  52. }
  53. #crSettingsFooter button.saveButton {
  54. color: white;
  55. background-color: #409D69;
  56. }
  57. .crTermsContainer { display: inline-block; }
  58. #crSnippetHeader {
  59. float: left;
  60. padding-right: 10px;
  61. }
  62. `);
  63.  
  64. (function(Cleanreads) {
  65. 'use strict';
  66.  
  67. /** The group bookshelf ID to use as default clean check list */
  68. Cleanreads.CLEAN_READS_BOOKSHELF_ID = 5989;
  69.  
  70. /** The positive search terms when determining verdict */
  71. Cleanreads.POSITIVE_SEARCH_TERMS = [
  72. { term: 'clean', exclude: { before: ['not', 'isn\'t'], after: ['ing'] }},
  73. { term: 'no sex', exclude: { before: [], after: [] }}
  74. ];
  75.  
  76. /** The negative search terms when determining verdict */
  77. Cleanreads.NEGATIVE_SEARCH_TERMS = [
  78. { term: 'sex', exclude: { before: ['no'], after: ['ist'] }},
  79. { term: 'adult', exclude: { before: ['young', 'new'], after: []}}
  80. ];
  81.  
  82. Cleanreads.SNIPPET_HALF_LENGTH = 65;
  83. Cleanreads.ATTEMPTS = 10;
  84.  
  85. /**
  86. * Load the settings from local storage if existant
  87. */
  88. Cleanreads.loadSettings = function() {
  89. try {
  90. Cleanreads.POSITIVE_SEARCH_TERMS = JSON.parse(localStorage.getItem("Cleanreads.POSITIVE_SEARCH_TERMS")) || Cleanreads.POSITIVE_SEARCH_TERMS;
  91. Cleanreads.NEGATIVE_SEARCH_TERMS = JSON.parse(localStorage.getItem("Cleanreads.NEGATIVE_SEARCH_TERMS")) || Cleanreads.NEGATIVE_SEARCH_TERMS;
  92. Cleanreads.SNIPPET_HALF_LENGTH = JSON.parse(localStorage.getItem("Cleanreads.SNIPPET_HALF_LENGTH")) || Cleanreads.SNIPPET_HALF_LENGTH;
  93. Cleanreads.ATTEMPTS = JSON.parse(localStorage.getItem("Cleanreads.ATTEMPTS")) || Cleanreads.ATTEMPTS;
  94. Cleanreads.CLEAN_READS_BOOKSHELF = JSON.parse(localStorage.getItem("Cleanreads.CLEAN_READS_BOOKSHELF")) || {
  95. books: [],
  96. timestamp: new Date(0),
  97. unloaded: true
  98. };
  99.  
  100. // Get Clean Reads shelf clean books if not recently loaded (1 day)
  101. let now = new Date();
  102. if (now.setDate(now.getDate() - 1) > Cleanreads.CLEAN_READS_BOOKSHELF.timestamp) {
  103. Cleanreads.getGroupBookshelfBooks(Cleanreads.CLEAN_READS_BOOKSHELF_ID, 5000)
  104. .then(data=> {
  105. Cleanreads.CLEAN_READS_BOOKSHELF = {
  106. books: data,
  107. timestamp: new Date()
  108. };
  109. localStorage.setItem("Cleanreads.CLEAN_READS_BOOKSHELF", JSON.stringify(Cleanreads.CLEAN_READS_BOOKSHELF));
  110. })
  111. .finally(Cleanreads.searchBookshelf);
  112. }
  113.  
  114. let settingsBody = document.getElementById("crSettingsBody");
  115. if (settingsBody) {
  116. settingsBody.innerHTML = `
  117. <div class="userInfoBoxContent">
  118. <div id="crSettingsTermButtons">
  119. </div>
  120. <h1 class="crSettingsHeader">Positive Search Terms:</h1>
  121. <div id="crPositiveSearchTerms">
  122. </div>
  123. <h1 class="crSettingsHeader">Negative Search Terms:</h1>
  124. <div id="crNegativeSearchTerms">
  125. </div>
  126. <h1 class="crSettingsHeader">Other Settings:</h1>
  127. <h4 id="crSnippetHeader">Snippet length:</h4> <input id="crSnippetHalfLength" type="number" value="${Cleanreads.SNIPPET_HALF_LENGTH}" min="0" />
  128. <h4 id="crAttemptsHeader">Max Verdict Load Attempts (tries every second):</h4> <input id="crAttempts" type="number" value="${Cleanreads.ATTEMPTS}" min="1" />
  129. </div>
  130. `;
  131.  
  132. // Add buttons
  133. let addPositiveButton = document.createElement("button");
  134. addPositiveButton.innerText = "Add Positive";
  135. addPositiveButton.className = "gr-button";
  136. addPositiveButton.onclick = Cleanreads.addSearchTerm.bind(null, true, null, null, null);
  137. document.getElementById("crSettingsTermButtons").appendChild(addPositiveButton);
  138. let addNegativeButton = document.createElement("button");
  139. addNegativeButton.innerText = "Add Negative";
  140. addNegativeButton.className = "gr-button";
  141. addNegativeButton.onclick = Cleanreads.addSearchTerm.bind(null, false, null, null, null);
  142. document.getElementById("crSettingsTermButtons").appendChild(addNegativeButton);
  143. let resetButton = document.createElement("button");
  144. resetButton.innerText = "Reset";
  145. resetButton.className = "gr-button";
  146. resetButton.onclick = function() {
  147. if (confirm("Are you sure you want to remove? You will have to refresh the page to see default values loaded.")) {
  148. localStorage.removeItem("Cleanreads.POSITIVE_SEARCH_TERMS");
  149. localStorage.removeItem("Cleanreads.NEGATIVE_SEARCH_TERMS");
  150. localStorage.removeItem("Cleanreads.SNIPPET_HALF_LENGTH");
  151. localStorage.removeItem("Cleanreads.ATTEMPTS");
  152. Cleanreads.loadSettings();
  153. }
  154. }
  155. document.getElementById("crSettingsTermButtons").appendChild(resetButton);
  156.  
  157. // Add existing terms
  158. Cleanreads.POSITIVE_SEARCH_TERMS.forEach((search) => Cleanreads.addSearchTerm(true, search.term, search.exclude.before, search.exclude.after));
  159. Cleanreads.NEGATIVE_SEARCH_TERMS.forEach((search) => Cleanreads.addSearchTerm(false, search.term, search.exclude.before, search.exclude.after));
  160. }
  161. } catch (ex) {
  162. console.error("Cleanreads: Failed to load settings!", ex);
  163. }
  164. };
  165.  
  166. /**
  167. * Save the positive and negative search terms to local storage
  168. */
  169. Cleanreads.saveSettings = function() {
  170. let positiveTerms = document.querySelectorAll("#crPositiveSearchTerms > .crTermsContainer");
  171. let negativeTerms = document.querySelectorAll("#crNegativeSearchTerms > .crTermsContainer");
  172.  
  173. Cleanreads.POSITIVE_SEARCH_TERMS = [...positiveTerms].map((search) => {
  174. return {
  175. term: search.querySelector("[name=term]").value,
  176. exclude: {
  177. before: search.querySelector("[name=excludeBefore]").value.split(",").map(x => x.trim()),
  178. after: search.querySelector("[name=excludeAfter]").value.split(",").map(x => x.trim())
  179. }
  180. }
  181. }).filter(x => x.term);
  182.  
  183. Cleanreads.NEGATIVE_SEARCH_TERMS = [...negativeTerms].map((search) => {
  184. return {
  185. term: search.querySelector("[name=term]").value,
  186. exclude: {
  187. before: search.querySelector("[name=excludeBefore]").value.split(",").map(x => x.trim()),
  188. after: search.querySelector("[name=excludeAfter]").value.split(",").map(x => x.trim())
  189. }
  190. }
  191. }).filter(x => x.term);
  192.  
  193. Cleanreads.SNIPPET_HALF_LENGTH = parseInt(document.getElementById("crSnippetHalfLength").value) || Cleanreads.SNIPPET_HALF_LENGTH;
  194. Cleanreads.ATTEMPTS = parseInt(document.getElementById("crAttempts").value) || Cleanreads.ATTEMPTS;
  195.  
  196. localStorage.setItem("Cleanreads.POSITIVE_SEARCH_TERMS", JSON.stringify(Cleanreads.POSITIVE_SEARCH_TERMS));
  197. localStorage.setItem("Cleanreads.NEGATIVE_SEARCH_TERMS", JSON.stringify(Cleanreads.NEGATIVE_SEARCH_TERMS));
  198. localStorage.setItem("Cleanreads.SNIPPET_HALF_LENGTH", JSON.stringify(Cleanreads.SNIPPET_HALF_LENGTH));
  199. localStorage.setItem("Cleanreads.ATTEMPTS", JSON.stringify(Cleanreads.ATTEMPTS));
  200. Cleanreads.loadSettings();
  201. }
  202.  
  203. /**
  204. * Setup the settings modal for Cleanreads
  205. */
  206. Cleanreads.setupSettings = function() {
  207. // Add link to menu dropdown
  208. let links = Array.from(document.getElementsByClassName('menuLink')).filter(x => x.innerText == 'Account settings');
  209. if (links && links.length) {
  210. let li = document.createElement('li');
  211. li.className = 'menuLink';
  212. li.onclick = Cleanreads.showSettings;
  213. li.innerHTML = `<a href='#' class='siteHeader__subNavLink'>Cleanreads settings</a>`;
  214. links[0].parentNode.insertBefore(li, links[0].nextSibling);
  215. }
  216. // Add dialog
  217. document.body.innerHTML += `
  218. <div id="crSettingsDialog">
  219. <div id="crSettingsHeader"><h1>Cleanreads Settings</h1></div>
  220. <div id="crSettingsBody">
  221. </div>
  222. <div id="crSettingsFooter"></div>
  223. </div>
  224. `;
  225. // Add link to profile page
  226. let settingsLink = document.createElement('a');
  227. settingsLink.href = '#';
  228. settingsLink.innerText = 'Cleanreads settings';
  229. settingsLink.onclick = Cleanreads.showSettings;
  230. document.getElementsByClassName('userInfoBoxContent')[0].appendChild(settingsLink);
  231. // Add close button to dialog
  232. let closeButton = document.createElement('button');
  233. closeButton.innerText = 'Close';
  234. closeButton.className = 'gr-button';
  235. closeButton.onclick = Cleanreads.hideSettings;
  236. document.getElementById('crSettingsFooter').appendChild(closeButton);
  237. // Add save button to dialog
  238. let saveButton = document.createElement('button');
  239. saveButton.innerText = 'Save';
  240. saveButton.className = 'gr-button saveButton';
  241. saveButton.onclick = Cleanreads.saveSettings;
  242. document.getElementById('crSettingsFooter').appendChild(saveButton);
  243. Cleanreads.loadSettings();
  244. };
  245.  
  246. /**
  247. * Add a search term to the settings UI
  248. */
  249. Cleanreads.addSearchTerm = function(positive, term, before, after) {
  250. document.getElementById(`cr${positive ? 'Positive' : 'Negative'}SearchTerms`).insertAdjacentHTML("beforeend",
  251. `<div class="crTermsContainer">
  252. <input name="excludeBefore" value="${before ? before.join(", ") : ''}" type="text" />
  253. <input name="term" value="${term || ''}" type="text" />
  254. <input name="excludeAfter" value="${after ? after.join(", ") : ''}" type="text" />
  255. </div>`);
  256. };
  257.  
  258. /**
  259. * Setup the rating (verdict) container on a book page
  260. */
  261. Cleanreads.setupRating = function() {
  262. let match = window.location.pathname.match(/book\/show\/(\d+)/);
  263. if (match && match.length > 1) {
  264. Cleanreads.loadSettings();
  265. Cleanreads.reviews = [];
  266. Cleanreads.positives = 0;
  267. Cleanreads.negatives = 0;
  268.  
  269. // Create container for rating
  270. let container = document.getElementById('descriptionContainer');
  271. let contentDescription = document.createElement('div');
  272. contentDescription.id = 'contentDescription';
  273. contentDescription.className = 'readable stacked u-bottomGrayBorder u-marginTopXSmall u-paddingBottomXSmall';
  274. contentDescription.innerHTML = `
  275. <h2 class="buyButtonContainer__title u-inlineBlock">Cleanreads Rating</h2>
  276. <h2 class="buyButtonContainer__title">
  277. Verdict: <span id="crVerdict">Loading...</span>
  278. (<span id="crPositives" class="contentClean">0</span>/<span id="crNegatives" class="contentNotClean">0</span>)
  279. </h2>
  280. <a id='expandCrDetails' href="#">(Details)</a>
  281. <div id="crDetails" style="display:none"></div>
  282. `;
  283. container.parentNode.insertBefore(contentDescription, container.nextSibling);
  284. Cleanreads.crDetails = document.getElementById('crDetails');
  285. document.getElementById('expandCrDetails').onclick = Cleanreads.expandDetails;
  286. Cleanreads.startReviews();
  287. }
  288. };
  289.  
  290. /**
  291. * Start attempting to get the available reviews on the page and read their content
  292. */
  293. Cleanreads.startReviews = function() {
  294. Cleanreads.getReviews();
  295. // Reviews are delayed content so keep looking for a bit if nothing
  296. if (!Cleanreads.reviews.length && Cleanreads.ATTEMPTS--) {
  297. setTimeout(Cleanreads.startReviews, 1000);
  298. } else {
  299. Cleanreads.calculateContent();
  300. }
  301. };
  302.  
  303. /**
  304. * Get reviews from page (only gets the first page of reviews, not easy to access others without API)
  305. */
  306. Cleanreads.getReviews = function() {
  307. let reviewElements = document.querySelectorAll('#reviews .reviewText');
  308. Cleanreads.reviews = Array.from(reviewElements).map(x => (x.querySelector('[style]') || x).innerText.trim());
  309. };
  310.  
  311. /**
  312. * Get title as text with series appended
  313. */
  314. Cleanreads.getTitle = function() {
  315. return document.getElementById('bookTitle').innerText.trim() + document.getElementById('bookSeries').innerText.trim();
  316. };
  317.  
  318. /**
  319. * Get book description text
  320. */
  321. Cleanreads.getDescription = function() {
  322. let description = document.getElementById('description');
  323. return (description.querySelector('[style]') || description).innerText.trim();
  324. };
  325.  
  326. /**
  327. * Get group bookshelf titles
  328. * @param {string} shelfId - The bookshelf id
  329. * @param {number} maxCount - The maximum number of books in the bookshelf to return
  330. * @returns {Promise} - A promise that resolves to array of book ids or rejects with error
  331. */
  332. Cleanreads.getGroupBookshelfBooks = function(shelfId, maxCount) {
  333. return new Promise(function(resolve, reject) {
  334. jQuery.ajax(`${window.location.origin}/group/bookshelf/${shelfId}?utf8=✓&view=covers&per_page=${maxCount || 1000}`)
  335. .done(result => {
  336. resolve(jQuery(result).find(".rightContainer div > a").toArray().map(x => (x.href.match(/show\/(\d*)/)||[])[1]));
  337. })
  338. .fail(err => reject(err));
  339. });
  340. }
  341.  
  342. /**
  343. * Get list titles
  344. * TODO: currently only gets first page
  345. * @param {string} listId - The list id
  346. * @returns {Promise} - A promise that resolves to array of book ids or rejects with error
  347. */
  348. Cleanreads.getListBooks = function(listId) {
  349. return new Promise(function(resolve, reject) {
  350. jQuery.ajax(`${window.location.origin}/list/show/${listId}`)
  351. .done(result => {
  352. resolve(jQuery(result).find(".tableList tr td:nth-child(2) div:nth-child(1)").toArray().map(x => x.id))
  353. })
  354. .fail(err => {
  355. reject(err);
  356. });
  357. });
  358. };
  359.  
  360. /**
  361. * Calculate the cleanliness
  362. */
  363. Cleanreads.calculateContent = function() {
  364. let count = 0, containing = [];
  365. // Insert containers for bases
  366. Cleanreads.crDetails.innerHTML +=
  367. `<h2 class="buyButtonContainer__title u-marginTopXSmall">Bookshelf Content Basis: </h2>
  368. <div id="bookshelfBasis">
  369. <i class="contentComment">
  370. Loading
  371. <a href="${window.location.origin}/group/bookshelf/${Cleanreads.CLEAN_READS_BOOKSHELF_ID}">Clean Reads bookshelf</a>
  372. </i>
  373. </div>`;
  374. Cleanreads.crDetails.innerHTML += `<h2 class="buyButtonContainer__title u-marginTopXSmall">Description Content Basis: </h2><div id="descriptionBasis"></div>`;
  375. Cleanreads.crDetails.innerHTML += `<h2 class="buyButtonContainer__title u-marginTopXSmall">Clean Basis: </h2><div id="cleanBasis"></div>`;
  376. Cleanreads.crDetails.innerHTML += `<h2 class="buyButtonContainer__title u-marginTopXSmall">Not Clean Basis: </h2><div id="notCleanBasis"></div>`;
  377. // Get containers
  378. let descriptionBasis = document.getElementById('descriptionBasis'),
  379. cleanBasis = document.getElementById('cleanBasis'),
  380. notCleanBasis = document.getElementById('notCleanBasis');
  381. // Search description
  382. let description = `Title: ${Cleanreads.getTitle()}\nDescription: ${Cleanreads.getDescription()}`;
  383. Cleanreads.POSITIVE_SEARCH_TERMS.forEach(term => Cleanreads.searchContent(term, description, descriptionBasis, true));
  384. Cleanreads.NEGATIVE_SEARCH_TERMS.forEach(term => Cleanreads.searchContent(term, description, descriptionBasis, false));
  385. // Search reviews
  386. Cleanreads.reviews.forEach(review => {
  387. Cleanreads.POSITIVE_SEARCH_TERMS.forEach(term => Cleanreads.searchContent(term, review, cleanBasis, true));
  388. Cleanreads.NEGATIVE_SEARCH_TERMS.forEach(term => Cleanreads.searchContent(term, review, notCleanBasis, false));
  389. });
  390. // Fill bases if nothing
  391. if (!descriptionBasis.innerHTML) {
  392. descriptionBasis.innerHTML = '<i class="contentComment">None</i>';
  393. }
  394. if (!cleanBasis.innerHTML) {
  395. cleanBasis.innerHTML = '<i class="contentComment">None</i>';
  396. }
  397. if (!notCleanBasis.innerHTML) {
  398. notCleanBasis.innerHTML = '<i class="contentComment">None</i>';
  399. }
  400.  
  401. // Update Clean Reads verdict
  402. if (!Cleanreads.CLEAN_READS_BOOKSHELF.unloaded) {
  403. Cleanreads.updateVerdict();
  404. Cleanreads.searchBookshelf();
  405. }
  406. };
  407.  
  408. /**
  409. * Search text for a given term, add found position to given container and increment positive/negative verdict
  410. * @param {string} term - The search term
  411. * @param {string} content - The content to search
  412. * @param {element} container - The dom element to append result to
  413. * @param {boolean} positive - Flag if positive or negative search term to determine result
  414. */
  415. Cleanreads.searchContent = function(term, content, container, positive) {
  416. let regex = new RegExp(`(^|[^(${term.exclude.before.join`|`}|\\s*)])(\\W*)(${term.term})(\\W*)($|[^(${term.exclude.after.join`|`}|\\s*)])`);
  417. let contentMatch = content.toLowerCase().match(regex);
  418. if (contentMatch) {
  419. positive ? Cleanreads.positives++ : Cleanreads.negatives++;
  420. let index = contentMatch.index + contentMatch[1].length + contentMatch[2].length;
  421. container.innerHTML += `
  422. <div class="contentComment">
  423. ...${content.slice(index - Cleanreads.SNIPPET_HALF_LENGTH, index)}<b class="content${positive ? '' : 'Not'}Clean">${
  424. content.substr(index, contentMatch[3].length)
  425. }</b>${content.slice(index + contentMatch[3].length, index + Cleanreads.SNIPPET_HALF_LENGTH)}...
  426. </div>`;
  427. }
  428. };
  429.  
  430. /**
  431. * Search the loaded bookshelf book ids for current book and update verdict
  432. */
  433. Cleanreads.searchBookshelf = function() {
  434. let bookId = window.location.pathname.match(/show\/(\d*)/)[1];
  435. let bookshelfBasis = document.getElementById('bookshelfBasis');
  436. if (bookId && Cleanreads.CLEAN_READS_BOOKSHELF.books.indexOf(bookId) != -1) {
  437. bookshelfBasis.innerHTML =
  438. `<div class="contentClean">
  439. Found in
  440. <a href="${window.location.origin}/group/bookshelf/${Cleanreads.CLEAN_READS_BOOKSHELF_ID}">Clean Reads bookshelf</a>
  441. </div>`;
  442. Cleanreads.positives++;
  443. Cleanreads.updateVerdict(true);
  444. } else {
  445. bookshelfBasis.innerHTML =
  446. `<div class="contentNotClean">
  447. Not found in
  448. <a href="${window.location.origin}/group/bookshelf/${Cleanreads.CLEAN_READS_BOOKSHELF_ID}">Clean Reads bookshelf</a>
  449. </div>`;
  450. Cleanreads.updateVerdict();
  451. }
  452. };
  453.  
  454. /**
  455. * Update the verdict shown in UI on the book
  456. * @param {boolean} overrideClean - If true, always set clean, but preserve positive/negative count
  457. */
  458. Cleanreads.updateVerdict = function(overrideClean) {
  459. let verdict = document.getElementById('crVerdict');
  460. if (overrideClean || (Cleanreads.positives && Cleanreads.positives > Cleanreads.negatives)) {
  461. verdict.innerText = `${Cleanreads.negatives ? 'Probably' : 'Most likely'} clean`;
  462. verdict.className += 'contentClean';
  463. } else if (Cleanreads.negatives && Cleanreads.negatives > Cleanreads.positives) {
  464. verdict.innerText = `${Cleanreads.positives ? 'Probably' : 'Most likely'} not clean`;
  465. verdict.className += 'contentNotClean';
  466. } else {
  467. verdict.innerText = Cleanreads.positives && Cleanreads.negatives ? 'Could be clean or not clean' : 'Unknown';
  468. verdict.className += 'contentUnknown';
  469. }
  470. document.getElementById('crPositives').innerText = Cleanreads.positives;
  471. document.getElementById('crNegatives').innerText = Cleanreads.negatives;
  472. };
  473.  
  474. /**
  475. * Expand the details section of Cleanreads verdict
  476. */
  477. Cleanreads.expandDetails = function() {
  478. let collapsedText = '(Details)',
  479. expandedText = '(Hide)';
  480. if (this.innerText == collapsedText) {
  481. Cleanreads.crDetails.style.display = 'block';
  482. this.innerText = expandedText;
  483. } else if (this.innerText == expandedText) {
  484. Cleanreads.crDetails.style.display = 'none';
  485. this.innerText = collapsedText;
  486. }
  487. };
  488.  
  489. /**
  490. * Show the settings modal for Cleanreads
  491. */
  492. Cleanreads.showSettings = function() {
  493. document.getElementById("crSettingsDialog").style.display = 'block';
  494. return false;
  495. };
  496.  
  497. /**
  498. * Hide the settings modal for Cleanreads
  499. */
  500. Cleanreads.hideSettings = function() {
  501. document.getElementById("crSettingsDialog").style.display = 'none';
  502. return false;
  503. };
  504.  
  505. // Loading. If on a book load the verdict, else if on a user page load settings
  506. if (window.location.href.match("/book/")) {
  507. Cleanreads.setupRating();
  508. } else if (window.location.href.match("/user/")) {
  509. Cleanreads.setupSettings()
  510. }
  511. })(window.Cleanreads = window.Cleanreads || {});