Advertisement
Guest User

8kun Baker tools v0.5.1

a guest
Jan 9th, 2020
1,091
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.    ==8Kun Baker Tools==
  3.    Version 0.5.1
  4.  
  5.   ==Features:==
  6.   '''Notables'''
  7.   * Highlight posts that are marked notable (I.E. someone has replied and said
  8.     notable) in Yellow
  9.   * Highlight nominating posts in Pink
  10.   * Highlight nominating posts in posts mentions in Green
  11.   * Filter to only nominating and notable posts, Q posts, Q replies
  12.   * Generate notables post
  13.   * Adds "Notable Nomination" button to posts that opens the
  14.     Quick Reply box and prefills it with a BAKER NOTABLE Template
  15.   * Easy access to Breads
  16.  
  17.   '''Q Posts'''
  18.     * Highlights Q Posts with white BG -> DARK TO LIGHT!
  19.     * Highlights Q posts in mentions (I.E. posts that get (YOU)'ed)
  20.     * Highlights links to Q Posts
  21.     * Cycle through Q Posts
  22.  
  23.   '''Comfyness'''
  24.     * Highlight PB links
  25.     * Thread stats overlay with
  26.       * color coded reply count that goes from green to red as bread ages
  27.       * UID Count
  28.       * Jump To Bottom Link
  29.       * Jump To Bottom Top link
  30.     * Option to blur images until hover
  31.     * Cycle through (You)'s
  32.     * Cycle through own posts
  33.  
  34.   ==To Install:==
  35.   1. Copy this source code
  36.   2. Go to 8kun
  37.   3. Click "Options" in the top right
  38.   4. Choose "User JS" tab
  39.   5. Paste Baker tools JS
  40.   6. WWG1WGA
  41.  
  42.   ==Changelog:==
  43.   '''0.5.0'''
  44.   * Option to show Q/(YOU)/Own Post navigation controls in the boardlist
  45.   * Option to hide Notable nomination button
  46.   * List of research breads
  47.   * BakerTools settings are now saved in local storage
  48.  
  49.   '''0.4.0'''
  50.   * Option to blur images until hover
  51.   * Adds a "Notable Nomination" button to posts that opens the Quick Reply
  52.     box and prefills it with a BAKER NOTABLE Template
  53.   * Add Q Post navigation links to the Baker Window
  54.   * Add (You) navigation links to the Baker Window
  55.   * Add own post navigation links to the Baker Window
  56.   * Cleaned up baker window design
  57.  
  58.   * More code cleanup and linting changes
  59.  
  60.   '''0.3.0'''
  61.   * Highlights Q Posts with white BG -> DARK TO LIGHT!
  62.   * Highlights Q posts in mentions (I.E. posts that get (YOU)'ed)
  63.   * Highlights links to Q Posts
  64.  
  65.   * Refactored code into classes for easier maint.
  66.  
  67.   '''0.2.0'''
  68.   * Highlight pb links
  69.   * Thread stats overlay with
  70.       * color coded reply count that goes from green to red as bread ages
  71.       * UID Count
  72.       * Jump To Bottom Link
  73.       * Jump To Bottom Top link
  74.  
  75.   '''0.1.0'''
  76.   Initial release:
  77.   * Highlight notables and nominators
  78.   * Filter to only show notables and nominators
  79.   * Create notables post
  80.  
  81.   Version History:
  82.   https://pastebin.com/SkS8etN9 0.5.0
  83.   https://pastebin.com/i9sF0Rd3 0.4.0
  84.   https://pastebin.com/kz9LrcE9 0.3.0
  85.   https://pastebin.com/4aEFsPwK 0.2.0
  86.   https://pastebin.com/eNmTtzdi 0.1.0
  87.  
  88. TODO: Nope button
  89. TODO: false positive notables and nominations
  90. TODO: Get notable description of pb notables
  91. TODO: Create Dough?
  92. TODO: Flood detect
  93. */
  94. (function($) {
  95. "use strict";
  96.  
  97. /**
  98.  * Wrapper for 8kun active_page variable to determine the type of
  99.  * page the user is on.
  100.  */
  101. class ActivePage {
  102.   /**
  103.    * Are we currently on the thread index page?
  104.    * @return {boolean} True if on index
  105.    */
  106.   static isIndex() {
  107.     return window.active_page == ActivePage.Index;
  108.   }
  109.  
  110.   /**
  111.    * Are we currently on the thread catalog page?
  112.    * @return {boolean} True if on catalog
  113.    */
  114.   static isCatalog() {
  115.     return window.active_page == ActivePage.Catalog;
  116.   }
  117.  
  118.   /**
  119.    * Are we on a thread page?
  120.    * @return {boolean} True if on thread
  121.    */
  122.   static isThread() {
  123.     console.log(window.active_page == ActivePage.Thread);
  124.     return window.active_page == ActivePage.Thread;
  125.   }
  126. }
  127. ActivePage.Index = 'index';
  128. ActivePage.Catalog = 'catalog';
  129. ActivePage.Thread = 'thread';
  130.  
  131.  
  132. /* global $ */
  133. /**
  134.  * Creates first, prev, next, last navigation controls
  135.  */
  136. class NavigationControl {
  137.   /**
  138.    * Construct navigatio control manager object
  139.    *
  140.    * @param {string} label the label for the control
  141.    * @param {Function} updateFunction Called to get latest data
  142.    * @param {string} updateEventName Called to get latest data
  143.    */
  144.   constructor(label, updateFunction, updateEventName) {
  145.     const strippedName = label.replace(/(\s|\(|\)|'|"|:)/g, '');
  146.     this.label = label;
  147.     this.list = updateFunction();
  148.     $(document).on(updateEventName, () => this.list = updateFunction());
  149.  
  150.     this.currentIndex = -1;
  151.     this.navigationClass = `bakertools-navcontrol-${strippedName}`;
  152.     this.indexChangeEvent =
  153.       `bakertools-navcontrol-${strippedName}-index-changed`;
  154.     this.currentIndexClass = `${this.navigationClass}-current-index`;
  155.     this.totalClass = `${this.navigationClass}-total`;
  156.     this.goToFirstClass = `${this.navigationClass}-goto-first`;
  157.     this.goToPreviousClass = `${this.navigationClass}-goto-prev`;
  158.     this.goToNextClass = `${this.navigationClass}-goto-next`;
  159.     this.goToLastClass = `${this.navigationClass}-goto-last`;
  160.  
  161.     this._setupStyles();
  162.     this._createElement();
  163.     this._setupListeners();
  164.   }
  165.  
  166.   /**
  167.    * setup styles for nav control
  168.    */
  169.   _setupStyles() {
  170.     const sheet = window.document.styleSheets[0];
  171.  
  172.     sheet.insertRule(`.boardlist
  173.       .${NavigationControl.containerClass}:before {
  174.       content: '[';
  175.       color: #89A;
  176.     }`, sheet.cssRules.length);
  177.  
  178.     sheet.insertRule(`.boardlist .${NavigationControl.containerClass}:after {
  179.       content: ']';
  180.       color: #89A;
  181.     }`, sheet.cssRules.length);
  182.  
  183.     sheet.insertRule(`.boardlist .${NavigationControl.containerClass} {
  184.       color: rgb(52, 52, 92);
  185.     }`, sheet.cssRules.length);
  186.   }
  187.  
  188.   /**
  189.    * Create nav element
  190.    */
  191.   _createElement() {
  192.     this.element = $(`
  193.     <span title="Navigate ${this.label}"
  194.         class="${NavigationControl.containerClass}">
  195.  
  196.         <label for="${this.navigationClass}">${this.label}:</label>
  197.         <span class="${this.navigationClass}
  198.                ${NavigationControl.navigationControlClass}">
  199.  
  200.                 <i class="fa fa-fast-backward ${this.goToFirstClass}"></i>
  201.                 <i class="fa fa-backward ${this.goToPreviousClass}"></i>
  202.  
  203.                 <span class="${this.currentIndexClass}">
  204.           ${this.currentIndex+1}
  205.         </span>
  206.         of
  207.         <span class="${this.totalClass}">${this.list.length}</span>
  208.  
  209.         <i class="fa fa-forward ${this.goToNextClass}"></i>
  210.         <i class="fa fa-fast-forward ${this.goToLastClass}"></i>
  211.       </span>
  212.     </span>
  213.     `).get(0);
  214.   }
  215.  
  216.   /**
  217.    * Setup button event listeners
  218.    */
  219.   _setupListeners() {
  220.     $(this.element).find('.'+this.goToFirstClass).click(function(e) {
  221.       this.goToFirstPost();
  222.     }.bind(this));
  223.  
  224.     $(this.element).find('.'+this.goToPreviousClass).click(function(e) {
  225.       this.goToPreviousPost();
  226.     }.bind(this));
  227.  
  228.     $(this.element).find('.'+this.goToNextClass).click(function(e) {
  229.       this.goToNextPost();
  230.     }.bind(this));
  231.  
  232.     $(this.element).find('.'+this.goToLastClass).click(function(e) {
  233.       this.goToLastPost();
  234.     }.bind(this));
  235.  
  236.     $(document).on(this.indexChangeEvent, function(e, index) {
  237.       if (this.currentIndex == index) return;
  238.       this.currentIndex = index;
  239.       this._setCurrentIndexControlValue(this.currentIndex + 1);
  240.     }.bind(this));
  241.   }
  242.  
  243.   /**
  244.    * Scroll to first post
  245.    */
  246.   goToFirstPost() {
  247.     console.info(`Go to first ${this.label} post`);
  248.     if (!this.list.length) {
  249.       return;
  250.     }
  251.     this.currentIndex = 0;
  252.     this.scrollToCurrentPost();
  253.   }
  254.  
  255.   /**
  256.    * Scroll to next navigated post
  257.    */
  258.   goToPreviousPost() {
  259.     console.info(`Go to prev ${this.label} post`);
  260.     if (!this.list.length) {
  261.       return;
  262.     }
  263.     if (this.currentIndex <= 0) {
  264.       this.currentIndex = this.list.length - 1;
  265.     } else {
  266.       this.currentIndex--;
  267.     }
  268.     this.scrollToCurrentPost();
  269.   }
  270.   /**
  271.    * Scroll to next navigated post
  272.    */
  273.   goToNextPost() {
  274.     console.info(`Go to next ${this.label} post`);
  275.     if (!this.list.length) {
  276.       return;
  277.     }
  278.     const lastPostIndex = this.list.length - 1;
  279.     if (this.currentIndex >= lastPostIndex) {
  280.       this.currentIndex = 0;
  281.     } else {
  282.       this.currentIndex++;
  283.     }
  284.     this.scrollToCurrentPost();
  285.   }
  286.  
  287.   /**
  288.    * Scroll the last  post in this bread into view
  289.    */
  290.   goToLastPost() {
  291.     console.info(`Go to last ${this.label} post`);
  292.     if (!this.list.length) {
  293.       return;
  294.     }
  295.     const numPosts = this.list.length;
  296.     this.currentIndex = numPosts - 1;
  297.     this.scrollToCurrentPost();
  298.   }
  299.  
  300.   /**
  301.    * Scrolls the current selected  post into view
  302.    */
  303.   scrollToCurrentPost() {
  304.     const post = this.list[this.currentIndex];
  305.     $(post).get(0).scrollIntoView();
  306.  
  307.     // Trigger events for other views of this data
  308.     $(document).trigger(this.navigationClass + '-index-updated',
  309.         this.currentIndex);
  310.  
  311.     // Update our local control
  312.     this._setCurrentIndexControlValue(this.currentIndex + 1);
  313.  
  314.     window.scrollBy(0, -20);
  315.   }
  316.  
  317.   /**
  318.    * Sets the value of the current index in the UI
  319.    * @param {number} val
  320.    */
  321.   _setCurrentIndexControlValue(val) {
  322.     $('.'+this.currentIndexClass).text(this.currentIndex+1);
  323.   }
  324. }
  325. NavigationControl.containerClass = `bakertools-navcontrol-container`;
  326. NavigationControl.navigationControlClass = 'bakertools-navigation-control';
  327.  
  328.  
  329.  
  330. /* global ResearchBread, $, NotableHighlighter */
  331. /**
  332. * Wrapper for a post nominated as notable
  333. */
  334. class NotablePost {
  335.   /**
  336.    * Construct an empty notable post object
  337.    */
  338.   constructor() {
  339.     this.element = null;
  340.     this.postNumber = null;
  341.     this.description = '[DESCRIPTION]';
  342.     this.nominatingPosts = [];
  343.   }
  344.  
  345.   /**
  346.    * Create a notable post from a nominating post
  347.    *
  348.    * @param {Element} nominatingPost A post that is nominating a notable
  349.    * @return {NotablePost} a Notable post or NullNotablePost if it fails
  350.    */
  351.   static fromNominatingPost(nominatingPost) {
  352.     const [postNumber, element] =
  353.       NotablePost._getNotableFromNominator(nominatingPost);
  354.  
  355.     if (!postNumber) {
  356.       console.info('Could not find a reply link in nominator post');
  357.       console.debug(nominatingPost.querySelector('.body').innerText);
  358.       if (!NotablePost.NULL) {
  359.         NotablePost.NULL = new NullNotablePost();
  360.       }
  361.       return NotablePost.NULL;
  362.     }
  363.  
  364.     let notable = NotablePost.findNotableByPostNumber(postNumber);
  365.     if (!notable) {
  366.       notable = new NotablePost();
  367.  
  368.       const isPreviousBread = element == null;
  369.       if (!isPreviousBread) {
  370.         notable.setElement(element);
  371.       } else {
  372.         // TODO: set pb description
  373.         // get the json from the post number
  374.         notable.postNumber = postNumber;
  375.       }
  376.       NotablePost._notables.push(notable);
  377.     }
  378.     notable.addNominatingPost(nominatingPost);
  379.  
  380.     return notable;
  381.   }
  382.  
  383.   /**
  384.    * Get the post number and the {Element} of a notable post from a
  385.    * nominating post
  386.    *
  387.    * @param {Element} nominatingPost a .div.post that nominates a notable
  388.    * @return {Array} Returns a list with the postNumber and the post element
  389.    */
  390.   static _getNotableFromNominator(nominatingPost) {
  391.     const postNumber = ResearchBread.getFirstReplyLink(nominatingPost);
  392.     const nominatedPost = document.querySelector('#reply_' + postNumber);
  393.     return [postNumber, nominatedPost];
  394.   }
  395.  
  396.   /**
  397.    * Is this a NullNotablePost
  398.    * @return {boolean} false
  399.    */
  400.   isNull() {
  401.     return false;
  402.   }
  403.  
  404.   /**
  405.    * @return {Array<NotablePost>} Array of the current notables
  406.    */
  407.   static getNotables() {
  408.     return NotablePost._notables;
  409.   }
  410.  
  411.   /**
  412.    * @arg {number} postNumber The post number of notable
  413.    * @return {NotablePost}
  414.    */
  415.   static findNotableByPostNumber(postNumber) {
  416.     return NotablePost._notables.find((notable) => notable.postNumber ==
  417.       postNumber);
  418.   }
  419.  
  420.   /**
  421.    * Set the element of the post
  422.    * @arg {Element} element
  423.    */
  424.   setElement(element) {
  425.     this.element = element;
  426.     this._markAsNotable(this.element);
  427.     this.description = element.querySelector('.body')
  428.         .innerText
  429.         .replace(/\n/g, ' ');
  430.     this.postNumber = $(this.element).find('.intro .post_no')
  431.         .text()
  432.         .replace('No.', '');
  433.     console.debug(this.postNumber);
  434.   }
  435.  
  436.   /**
  437.    * Get the reply shortlink for the post
  438.    * @return {string}
  439.    */
  440.   shortLink() {
  441.     return '>>' + this.postNumber;
  442.   }
  443.  
  444.   /**
  445.    * Add a nominator to the notable
  446.    *
  447.    * @param {Element} nominatingPost A .div.post that nominates this post
  448.    */
  449.   addNominatingPost(nominatingPost) {
  450.     this.nominatingPosts.push(nominatingPost);
  451.     this._markAsNominator(nominatingPost);
  452.     this._markNominatorInMentions(nominatingPost);
  453.   }
  454.  
  455.   /**
  456.    * @arg {Element} nominatorPost .post
  457.    */
  458.   _markAsNominator(nominatorPost) {
  459.     console.info(`Mark as nominator: ${nominatorPost}`);
  460.     nominatorPost.classList.add(NotableHighlighter.NOMINATOR_CLASS);
  461.   }
  462.  
  463.   /**
  464.    * @arg {Element} post .post
  465.    */
  466.   _markAsNotable(post) {
  467.     console.info(`Mark as notable: ${post}`);
  468.     post.classList.add(NotableHighlighter.NOTABLE_CLASS);
  469.   }
  470.  
  471.  
  472.   /**
  473.    * Gives links to nominators a special style in notable mentions
  474.    *
  475.    * @param {Element} nominatingPost A .div.post that is nominating this
  476.    *  notable
  477.    */
  478.   _markNominatorInMentions(nominatingPost) {
  479.     const notableString = this.element && this.element.id ||
  480.         this.postNumber + ' (pb)';
  481.     console.info(`Mark as nominator in mentions.  Notable: ${notableString},
  482.       Nominating Post: ${nominatingPost.id}`);
  483.     if (!this.element) {
  484.       console.info(`Notable post is null - possible pb/lb`);
  485.       return;
  486.     }
  487.     const nominatingPostId = nominatingPost.id.replace('reply_', '');
  488.     $(this.element).find('.mentioned-'+nominatingPostId)
  489.         .addClass(NotableHighlighter.NOMINATOR_CLASS);
  490.   }
  491. }
  492. NotablePost._notables = [];
  493. NotablePost.NULL = null; // NullNotablePost
  494.  
  495. /**
  496.  * A null post that is returned when the post cant be found
  497.  */
  498. class NullNotablePost extends NotablePost {
  499.   /**
  500.    * Returns true because this is a null post
  501.    * @return {boolean} true
  502.    */
  503.   isNull() {
  504.     return true;
  505.   }
  506. }
  507.  
  508.  
  509. /**
  510.  * Research Bread Class
  511.  */
  512. class ResearchBread {
  513.   /**
  514.    * Get an array of post bodies with dough posts filtered out
  515.    * @return {NodeList} of .post elements
  516.    */
  517.   static getPostsWithoutDough() {
  518.     const posts = Array.from(document
  519.         .querySelectorAll(ResearchBread.POST_SELECTOR));
  520.  
  521.     const filteredPosts = posts.filter(function(post) {
  522.       return !post.querySelector('.body')
  523.           .innerText.match(ResearchBread.DOUGH_POSTS_REGEX);
  524.     });
  525.  
  526.     return filteredPosts;
  527.   }
  528.  
  529.   /**
  530.    * Determine what the bread number is
  531.    * @return {number} the number of the research bread
  532.    */
  533.   static getBreadNumber() {
  534.     const breadNumberRegex = /#(.+?) /;
  535.     return document.querySelector(ResearchBread.OP_SUBJECT_SELECTOR)
  536.         .innerText
  537.         .match(breadNumberRegex)[1] || 'COULD NOT FIND BREAD NUMBER';
  538.   }
  539.  
  540.   /**
  541.    * @arg {Element} post .post
  542.    * @return {RegexMatch} Capture 1 is the number
  543.    */
  544.   static getFirstReplyLink(post) {
  545.     const match = post.querySelector('.body')
  546.         .innerHTML
  547.         .match(ResearchBread.REPLY_REGEX);
  548.     return match && match[1] || null;
  549.   }
  550. }
  551. ResearchBread.NEW_POST_EVENT = 'new_post';
  552. ResearchBread.OP_SUBJECT_SELECTOR = '.post.op > p > label > span.subject';
  553. ResearchBread.POST_BODY_SELECTOR = '.post > .body';
  554. ResearchBread.POST_SELECTOR = '.post';
  555. ResearchBread.REPLY_REGEX = /highlightReply\('(.+?)'/;
  556. ResearchBread.DOUGH_POSTS_REGEX = new RegExp(
  557.     `^(Welcome To Q Research General|` +
  558.     `Global Announcements|` +
  559.     `War Room` +
  560.     `|QPosts Archives).*`);
  561.  
  562.  
  563. /* globals $ */
  564. /* exported WindowElement */
  565. /**
  566.  * Class for windows
  567.  */
  568. class WindowElement {
  569.   /**
  570.    * Construct WindowElement
  571.    * @param {string} windowName
  572.    * @param {string} linkText
  573.    */
  574.   constructor(windowName, linkText) {
  575.     this.styleId = 'bakertools-WindowElement-basestyles';
  576.     this.id = `bakertools-${windowName}-window`;
  577.     this.linkText = linkText;
  578.     this.class = 'bakertools-WindowElement';
  579.     this.headerClass = 'bakertools-WindowElement-header';
  580.     this.windowCloseId = `bakertools-${windowName}-WindowElement-close`;
  581.     this.windowCloseClass = `bakertools-WindowElement-close`;
  582.     this.element = null;
  583.  
  584.     this._createWindowStyles();
  585.     this._createElement();
  586.     this._setupWindowLink();
  587.   }
  588.  
  589.   /**
  590.    * Create the window element
  591.    */
  592.   _createElement() {
  593.     this.element = document.createElement('div');
  594.     this.element.id = this.id;
  595.     $(this.element).addClass(this.class);
  596.  
  597.     this.element.innerHTML = `
  598.     <header class="${this.headerClass}">
  599.       <h3>${this.linkText}</h3>
  600.       <a id="${this.windowCloseId}" class='${this.windowCloseClass}'
  601.         href="javascript:void(0)">
  602.         <i class="fa fa-times"></i>
  603.       </a>
  604.     </header>
  605.     `;
  606.     document.body.appendChild(this.element);
  607.  
  608.     $(this.element).draggable();
  609.     $(this.element).hide();
  610.  
  611.     $('#'+this.windowCloseId).click(function(e) {
  612.       this.hide();
  613.     }.bind(this));
  614.   }
  615.  
  616.   /**
  617.    * Create CSS styles needed by the window
  618.    */
  619.   _createWindowStyles() {
  620.     if ($('#' + this.styleId).length) {
  621.       return;
  622.     }
  623.     $('head').append(`
  624.       <style id='${this.styleId}'>
  625.       .${this.class} {
  626.         width: 300px;
  627.         background-color: rgb(214, 218, 240);
  628.         position: fixed;
  629.         z-index: 100;
  630.         float: right;
  631.         right:28.25px;
  632.         border: 1px solid;
  633.       }
  634.  
  635.       .${this.class} .${this.headerClass} {
  636.         background: #98E;
  637.         border: solid 1px;
  638.         text-align: center;
  639.         margin: 0px;
  640.       }
  641.      
  642.       .${this.class} .${this.headerClass} h3 {
  643.         margin: 0;
  644.       }
  645.  
  646.       .${this.class} .${this.windowCloseClass} {
  647.         top: 0px;
  648.         right: 0px;
  649.         position: absolute;
  650.         margin-right: 3px;
  651.         font-size: 20px;
  652.       }
  653.  
  654.       .${this.class} details {
  655.         padding: 5px;
  656.       }
  657.  
  658.       .${this.class} summary {
  659.         margin: 0 0 8px;
  660.         font-weight: bold;
  661.         border-bottom: solid 2px;
  662.       }
  663.     </style>
  664.     `);
  665.   }
  666.  
  667.   /**
  668.    * Create link for show/hiding window, placed in boardlist bar
  669.    */
  670.   _setupWindowLink() {
  671.     this.link = document.createElement('a');
  672.     this.link.textContent = `[${this.linkText}]`;
  673.     this.link.style.cssText = 'float: right;';
  674.     this.link.title = this.linkText;
  675.     this.link.href = 'javascript:void(0)';
  676.     document.querySelector('.boardlist').appendChild(this.link);
  677.  
  678.     this.link.onclick = this.toggle.bind(this);
  679.   }
  680.  
  681.   /**
  682.    * Setup timeout for updating bread list
  683.    */
  684.   _setupListeners() {
  685.     // window.setTimeout(this.updateBreadList, 1000)
  686.   }
  687.  
  688.   /**
  689.    * Show the window
  690.    */
  691.   show() {
  692.     $(this.element).css({'top': 15});
  693.     $(this.element).show();
  694.   }
  695.  
  696.   /**
  697.    * Hide the window
  698.    */
  699.   hide() {
  700.     $(this.element).hide();
  701.   }
  702.  
  703.   /**
  704.    * Is the window visible?
  705.    * @return {boolean} true if window is visible
  706.    */
  707.   isVisible() {
  708.     return $(this.element).is(':visible');
  709.   }
  710.  
  711.   /**
  712.    * Toggle visibility of window
  713.    */
  714.   toggle() {
  715.     if (this.isVisible()) {
  716.       this.hide();
  717.     } else {
  718.       this.show();
  719.     }
  720.   }
  721. }
  722.  
  723. /* exported BakerWindow */
  724. /* global NavigationControl, $, WindowElement */
  725. /**
  726. * Baker Window
  727. */
  728. class BakerWindow extends WindowElement {
  729.   /**
  730.    * Construct Baker window element, register listeners
  731.    */
  732.   constructor() {
  733.     super('baker', 'Baker Tools');
  734.     this.bakerWindowStyleId = 'bakertools-bakerwindow-style';
  735.     this.bakerWindowOptionsId = 'bakertools-window-options';
  736.     this.bakerWindowNavigationId = 'bakertools-window-navigation';
  737.     this.bakerWindowBakerId = 'bakertools-window-baker';
  738.     this.bakerWindowBodyId = 'bakertools-bakerwindow-body';
  739.  
  740.     this._createStyles();
  741.     this._createBody();
  742.   }
  743.  
  744.   /**
  745.    * Create CSS styles needed by the window
  746.    */
  747.   _createStyles() {
  748.     if ($('#' + this.bakerWindowStyleId).length) {
  749.       return;
  750.     }
  751.     $('head').append(`
  752.     <style id='${this.bakerWindowStyleId}'>
  753.       #${this.id} #${this.bakerWindowNavigationId}
  754.       .${NavigationControl.containerClass} {
  755.         display: inline-block;
  756.         width: 100%;
  757.       }
  758.  
  759.       #${this.id} #${this.bakerWindowNavigationId}
  760.       .${NavigationControl.navigationControlClass} {
  761.         float: right;
  762.       }
  763.     </style>
  764.     `);
  765.   }
  766.  
  767.   /**
  768.    * Create the actual window HTML element
  769.    */
  770.   _createBody() {
  771.     $('#'+this.id).append(`
  772.     <form id="${this.bakerWindowBodyId}">
  773.       <details id='${this.bakerWindowOptionsId}' open>
  774.         <summary>Options</summary>
  775.       </details>
  776.       <details id='${this.bakerWindowNavigationId}' open>
  777.         <summary>Navigation</summary>
  778.       </details>
  779.       <details id='${this.bakerWindowBakerId}' open>
  780.         <summary>Baker Tools</summary>
  781.       </details>
  782.     </form>
  783.     `);
  784.   }
  785.  
  786.   /**
  787.    * Add form controls to options section of baker window
  788.    * @arg {Element} htmlContentString form controls
  789.    */
  790.   addOption(htmlContentString) {
  791.     $('#'+this.bakerWindowOptionsId).append(htmlContentString);
  792.   }
  793.  
  794.   /**
  795.    * Add html elements to the navigation section of the baker window
  796.    * @arg {Element} htmlContentString form controls
  797.    */
  798.   addNavigation(htmlContentString) {
  799.     $('#'+this.bakerWindowNavigationId).append(htmlContentString);
  800.   }
  801.  
  802.   /**
  803.    * Add html elements to the baker section of the baker window
  804.    * @arg {Element} htmlContentString form controls
  805.    */
  806.   addBaker(htmlContentString) {
  807.     $('#'+this.bakerWindowBakerId).append(htmlContentString);
  808.   }
  809. } // end class BakerWindow
  810.  
  811. /* global $ */
  812. /**
  813. * Blur images until highlighted
  814. */
  815. class BlurImages {
  816.   /**
  817.    * Construct blur images object and setup styles
  818.    */
  819.   constructor() {
  820.     this.blurImages = 'bakertools-blur-images';
  821.     this.blurImagesStyleId = 'bakertools-blur-images-style';
  822.     window.bakerTools.mainWindow.addOption(`
  823.       <label for="${this.blurImages}">Blur Images Until Hover</label>
  824.       <input type="checkbox" id="${this.blurImages}"
  825.         title="Blur images until mouse hover" /></br>
  826.     `);
  827.  
  828.     $('#'+this.blurImages).change(function(e) {
  829.       this.setBlurImages(e.target.checked);
  830.     }.bind(this));
  831.  
  832.     this._readSettings();
  833.   }
  834.  
  835.   /**
  836.    * Read settings from localStorage
  837.    */
  838.   _readSettings() {
  839.     this.setBlurImages(JSON.parse(
  840.         localStorage.getItem(
  841.             BlurImages.BLUR_IMAGES_SETTING),
  842.     ));
  843.   }
  844.  
  845.   /**
  846.    * Set whether or not images are blurred
  847.    * @param {boolean} blurImages if true, blur images
  848.    */
  849.   setBlurImages(blurImages) {
  850.     $('#'+this.blurImages).prop('checked',
  851.         blurImages);
  852.  
  853.     localStorage.setItem(BlurImages.BLUR_IMAGES_SETTING,
  854.         blurImages);
  855.  
  856.     if (blurImages) {
  857.       $(`<style id='${this.blurImagesStyleId}' type='text/css'>
  858.           .post-image {
  859.               filter: blur(5px);
  860.               transition: all 233ms;
  861.           }
  862.           .post-image:hover {
  863.               filter: blur(.5px);
  864.               transition: all 89ms;
  865.           }
  866.       </style>`).appendTo('head');
  867.     } else {
  868.       $(`#${this.blurImagesStyleId}`).remove();
  869.     }
  870.   }
  871. }
  872. BlurImages.BLUR_IMAGES_SETTING = 'bakertools-blur-images';
  873.  
  874.  
  875.  
  876. /* globals $, WindowElement */
  877. /* exported BreadList */
  878. /**
  879.  * Creates a list of breads for navigation comfyness
  880.  */
  881. class BreadList extends WindowElement {
  882.   /**
  883.    * Construct breadlist object
  884.    */
  885.   constructor() {
  886.     super('breadlist', 'Bread List');
  887.     $('#'+this.id).css('height', '400px');
  888.     this.breadListWindowHeaderId = 'bakertools-breadlist-window-header';
  889.     this.breadListWindowCloseId = 'bakertools-breadlist-window-close';
  890.     this.breadListWindowBody = 'bakertools-breadlist-window-body';
  891.     this.breadListTable = 'bakertools-breadlist-table';
  892.     this.lastUpdatedId = 'bakertools-breadlist-lastupdated';
  893.  
  894.     this._breads = [];
  895.     this.board = 'qresearch';
  896.     this.breadRegex = /(.+)\s+#(\d+):\s+(.+?$)/;
  897.     // /\(.+\) #\(\d+\): \(.+?$\)/;
  898.     this.indexPage = `${window.location.protocol}//${window.location.host}` +
  899.         `/${this.board}/`;
  900.  
  901.     this._createBody();
  902.     this._setupStyles();
  903.     this.updateBreadList();
  904.     this._setupListeners();
  905.   }
  906.  
  907.   /**
  908.    * setup table styles
  909.    */
  910.   _setupStyles() {
  911.     // https://stackoverflow.com/questions/21168521/table-fixed-header-and-scrollable-body
  912.     $('head').append(`
  913.     <style id='baketools-breadlist-window-styles'>
  914.       #${this.id} {
  915.         right: 380px;
  916.       }
  917.  
  918.       #${this.breadListWindowBody} {
  919.         overflow-y: auto;
  920.         height: 365px;
  921.         font-size: .8em;
  922.       }
  923.  
  924.       #${this.breadListTable} {
  925.         border-collapse: collapse;
  926.         border-spacing: 0px;
  927.       }
  928.  
  929.       #${this.breadListTable} thead th {
  930.         position: sticky;
  931.         top: 0;
  932.       }
  933.  
  934.       #${this.breadListTable} th,
  935.       #${this.breadListTable} td {
  936.         border: 1px solid #000;
  937.         border-top: 0;
  938.       }
  939.       #${this.breadListTable} thead th {
  940.         box-shadow: 1px 1px 0 #000;
  941.       }
  942.     </style>
  943.     `);
  944.   }
  945.  
  946.   /**
  947.    * Create the actual window HTML element
  948.    */
  949.   _createBody() {
  950.     $('#'+this.id).append(`
  951.     <div id='${this.breadListWindowBody}'>
  952.       <table id='${this.breadListTable}'>
  953.         <thead>
  954.           <tr>
  955.             <th>Group</th>
  956.             <th>No.</th>
  957.             <th>Bread</th>
  958.             <th>replies</th>
  959.           </tr>
  960.         </thead>
  961.         <tbody>
  962.         </tbody>
  963.       </table>
  964.     </div>
  965.     <footer>
  966.       Last Updated: <span id="${this.lastUpdatedId}"></span>
  967.     </footer>
  968.     `);
  969.   }
  970.  
  971.   /**
  972.    * Setup timeout for updating bread list
  973.    */
  974.   _setupListeners() {
  975.     window.setInterval(function(e) {
  976.       this.updateBreadList();
  977.     }.bind(this), 1000 * 60 * 2.5); // 2.5min update
  978.   }
  979.  
  980.   /**
  981.    * Get the list of breads
  982.    */
  983.   updateBreadList() {
  984.     this.breads = [];
  985.  
  986.     const promises = [];
  987.     for (let page = 0; page < 3; page++) {
  988.       promises.push(
  989.           $.getJSON(this.indexPage + `${page}.json`,
  990.               this.parseIndex.bind(this)),
  991.       );
  992.     }
  993.     Promise.all(promises).then(function() {
  994.       this.breads.sort(function(a, b) {
  995.         if (a.lastModified < b.lastModified) return -1;
  996.         if (a.lastModified == b.lastModified) return 0;
  997.         if (a.lastModified > b.lastModified) return 1;
  998.       }).reverse();
  999.       console.info(this.breads);
  1000.       this.populateBreadTable();
  1001.     }.bind(this));
  1002.   }
  1003.  
  1004.   /**
  1005.    * Parse index json for breads
  1006.    * @param {Object} index
  1007.    */
  1008.   parseIndex(index) {
  1009.     if (index && index.threads) {
  1010.       index.threads.forEach(function(thread) {
  1011.         const op = thread.posts[0];
  1012.         const match = op.sub.match(this.breadRegex);
  1013.  
  1014.         if (match) {
  1015.           const researchGroup = match[1];
  1016.           const breadNumber = match[2];
  1017.           const breadName = match[3];
  1018.           this.breads.push(new Bread(
  1019.               this.board,
  1020.               researchGroup,
  1021.               breadNumber,
  1022.               breadName,
  1023.               op.replies,
  1024.               op.no,
  1025.               op.last_modified,
  1026.           ));
  1027.         }
  1028.       }.bind(this)); // Index foreach
  1029.     } // if index and index.threads
  1030.   }
  1031.  
  1032.   /**
  1033.    * Populate the bread list table
  1034.    */
  1035.   populateBreadTable() {
  1036.     $(this.breadListTable).empty();
  1037.     this.breads.forEach(function(bread) {
  1038.       this._addBread(bread);
  1039.     }.bind(this));
  1040.  
  1041.     const lastUpdated = new Date();
  1042.     $('#'+this.lastUpdatedId).text(lastUpdated.toLocaleString());
  1043.   }
  1044.  
  1045.   /**
  1046.    * Add bread
  1047.    * @param {Bread} bread
  1048.    */
  1049.   _addBread(bread) {
  1050.     $('#'+this.breadListTable).append(`
  1051.       <tr>
  1052.         <td><a href='${bread.url}'>${bread.researchGroup}</a></td>
  1053.         <td><a href='${bread.url}'>${bread.researchNumber}</a></td>
  1054.         <td><a href='${bread.url}'>${bread.breadName}</a></td>
  1055.         <td><a href='${bread.url}'>${bread.replies}</a></td>
  1056.       </tr>
  1057.     `);
  1058.   }
  1059. }
  1060.  
  1061. /**
  1062.  * Represents a research bread
  1063.  */
  1064. class Bread {
  1065.   /**
  1066.    * Construct a bread
  1067.    *
  1068.    * @param {string} boardName
  1069.    * @param {string} researchGroup
  1070.    * @param {number} researchNumber
  1071.    * @param {string} breadName
  1072.    * @param {number} replies
  1073.    * @param {number} postId
  1074.    * @param {number} lastModified
  1075.    */
  1076.   constructor(boardName, researchGroup, researchNumber, breadName,
  1077.       replies, postId, lastModified) {
  1078.     this.boardName = boardName;
  1079.     this.researchGroup = researchGroup;
  1080.     this.researchNumber = researchNumber;
  1081.     this.breadName = breadName;
  1082.     this.replies = replies;
  1083.     this.postId = postId;
  1084.     this.lastModified = lastModified;
  1085.   }
  1086.  
  1087.   /**
  1088.    * Get bread url
  1089.    *
  1090.    * @return {string} url to bread
  1091.    */
  1092.   get url() {
  1093.     return `${window.location.protocol}//${window.location.host}` +
  1094.         `/${this.boardName}/res/${this.postId}.html`;
  1095.   }
  1096. }
  1097.  
  1098. /* global $, ResearchBread */
  1099. /**
  1100. * Add notable button to posts that opens quick reply
  1101. * and populates with a template message
  1102. */
  1103. class NominatePostButtons {
  1104.   /**
  1105.    * Construct NPB object and setup listeners
  1106.    */
  1107.   constructor() {
  1108.     this.nominateButtonClass = 'bakertools-nominate-button';
  1109.     this.hidePostNotableButtonCheckboxId = 'bakertools-hide-notables';
  1110.     this.bakerNotableHeader = '==BAKER NOTABLE==\n';
  1111.     this.notableReasonPlaceholder = '[REASON FOR NOTABLE HERE]';
  1112.  
  1113.     $('div.post.reply').each(function(i, post) {
  1114.       this._addButtonToPost(post);
  1115.     }.bind(this));
  1116.  
  1117.  
  1118.     this._setupBakerWindowControls();
  1119.     this._setupListeners();
  1120.     this._readSettings();
  1121.   }
  1122.  
  1123.   /**
  1124.    * Read settings from localStorage
  1125.    */
  1126.   _readSettings() {
  1127.     this.showNotableNominationButton(JSON.parse(
  1128.         localStorage.getItem(
  1129.             NominatePostButtons.HIDE_NOMINATE_BUTTON_SETTING),
  1130.     ));
  1131.   }
  1132.  
  1133.   /**
  1134.    * Add options to baker window
  1135.    */
  1136.   _setupBakerWindowControls() {
  1137.     window.bakerTools.mainWindow.addOption(`
  1138.     <br />
  1139.     <label for="${this.hidePostNotableButtonCheckboxId}"
  1140.       title="Hide post 'Notable' buttons" >
  1141.       Hide "Notable" buttons
  1142.     </label>
  1143.     <input type="checkbox" id="${this.hidePostNotableButtonCheckboxId}"
  1144.       title="Hide post 'Notable' buttons" /><br />
  1145.     `);
  1146.   }
  1147.  
  1148.   /**
  1149.    * Setup event listeners
  1150.    */
  1151.   _setupListeners() {
  1152.     $(document).on(ResearchBread.NEW_POST_EVENT, function(e, post) {
  1153.       this._addButtonToPost(post);
  1154.     }.bind(this));
  1155.  
  1156.     $('#'+this.hidePostNotableButtonCheckboxId).change(function(e) {
  1157.       this.showNotableNominationButton(e.target.checked);
  1158.     }.bind(this));
  1159.   }
  1160.  
  1161.   /**
  1162.    * Show or hide the notable nomination buttons
  1163.    *
  1164.    * @param {boolean} showNotableNominationButton
  1165.    */
  1166.   showNotableNominationButton(showNotableNominationButton) {
  1167.     $('#'+this.hidePostNotableButtonCheckboxId).prop('checked',
  1168.         showNotableNominationButton);
  1169.  
  1170.     localStorage.setItem(NominatePostButtons.HIDE_NOMINATE_BUTTON_SETTING,
  1171.         showNotableNominationButton);
  1172.  
  1173.     const styleId = 'baker-tools-notable-button-style';
  1174.     if (showNotableNominationButton) {
  1175.       $('head').append(`
  1176.         <style id='${styleId}'>
  1177.           .${this.nominateButtonClass} {
  1178.             display: none;
  1179.           }
  1180.       `);
  1181.     } else {
  1182.       $(`#${styleId}`).remove();
  1183.     }
  1184.   }
  1185.  
  1186.  
  1187.   /**
  1188.    * Add button to the provided post
  1189.    * @param {Element} post
  1190.    */
  1191.   _addButtonToPost(post) {
  1192.     const button = document.createElement('button');
  1193.     $(button).addClass(this.nominateButtonClass);
  1194.     $(button).append(`
  1195.       <i class="fa fa-star" style="color: goldenrod;"></i>
  1196.       Notable`);
  1197.  
  1198.     $(button).click(function(e) {
  1199.       const postNumber = $(post)
  1200.           .find('.intro .post_no')
  1201.           .text()
  1202.           .replace('No.', '');
  1203.       const href = $(post)
  1204.           .find('.intro .post_no')
  1205.           .get(0).href;
  1206.  
  1207.       // 8kun core - adds >>postnumber to- and unhides quickreply
  1208.       window.citeReply(postNumber, href);
  1209.  
  1210.       const quickReplyBody = $('#quick-reply #body');
  1211.       const oldText = quickReplyBody.val();
  1212.  
  1213.       quickReplyBody.val(oldText + this.bakerNotableHeader +
  1214.         this.notableReasonPlaceholder);
  1215.  
  1216.       // Don't ask me why i have to do this, ask CodeMonkeyZ
  1217.       // Not sure why citeReply which calls cite needs to set a timeout to
  1218.       // replace the body of the quickreply with itself.  We need to combat
  1219.       // that here
  1220.       // setTimeout(function() {
  1221.       //     var tmp = $('#quick-reply textarea[name="body"]').val();
  1222.       //     $('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
  1223.       //  }, 1);
  1224.       // $(window).on('cite', function(e, id, with_link) {
  1225.       // TODO: Figure this out
  1226.       const self = this;
  1227.       setTimeout(function() {
  1228.         quickReplyBody.select();
  1229.         quickReplyBody.prop('selectionStart',
  1230.             oldText.length + self.bakerNotableHeader.length);
  1231.       }, 1.2);
  1232.     }.bind(this));
  1233.  
  1234.     $(post).find('.post_modified').append(button);
  1235.   }
  1236. }
  1237. NominatePostButtons.HIDE_NOMINATE_BUTTON_SETTING =
  1238.     'bakertools-hide-nominate-button';
  1239.  
  1240. /* global $, ResearchBread, NotablePost */
  1241. /**
  1242. * Makes notable posts easier to see by highlighting posts that anons nominate
  1243. * as notable.
  1244. *
  1245. * If someone replies to a post and their post contains the word 'notable',
  1246. * the replied to post will be considered notable.
  1247. *
  1248. * Both the notable post and the nominator posts will be highlighted, as well
  1249. * as the nominator link in the notable's mentions will be highlighted.
  1250. */
  1251. class NotableHighlighter {
  1252.   /**
  1253.    * Construct notablehighlighter object, find and highlight
  1254.    * current notable sand setup listeners
  1255.    */
  1256.   constructor() {
  1257.     this.NOMINATING_REGEX = /notable/i;
  1258.  
  1259.     this.showOnlyNotablesCheckboxId = 'bakertools-show-only-notable';
  1260.     this.createNotablePostButtonId = 'bakertools-create-notable-post';
  1261.     this.notableEditorId = 'bakertools-notable-editor';
  1262.  
  1263.     this._createStyles();
  1264.     this._setupBakerWindowControls();
  1265.     this.findNominatedNotables();
  1266.     this._setupListeners();
  1267.     this._readSettings();
  1268.   }
  1269.  
  1270.   /**
  1271.    * Read settings from local storage
  1272.    */
  1273.   _readSettings() {
  1274.     this.setOnlyShowNotables(JSON.parse(
  1275.         localStorage.getItem(
  1276.             NotableHighlighter.ONLY_SHOW_NOTABLES_SETTING),
  1277.     ));
  1278.   }
  1279.  
  1280.   /**
  1281.    * Create styles that determine how notables are highlighted
  1282.    */
  1283.   _createStyles() {
  1284.     const sheet = window.document.styleSheets[0];
  1285.     sheet.insertRule(`div.post.${NotableHighlighter.NOTABLE_CLASS} {
  1286.       background-color: #FFFFCC;
  1287.     }`, sheet.cssRules.length);
  1288.     sheet.insertRule(`div.post.${NotableHighlighter.NOMINATOR_CLASS} {
  1289.       background-color: #FFCCE5;  
  1290.     }`, sheet.cssRules.length);
  1291.     sheet.insertRule(`
  1292.     div.post.reply .mentioned .${NotableHighlighter.NOMINATOR_CLASS} {
  1293.       color: #00CC00;
  1294.       font-weight: bold;
  1295.       font-size: 1.5em;
  1296.     }`, sheet.cssRules.length);
  1297.   }
  1298.  
  1299.   /**
  1300.    * Add controls to the bakerwindow
  1301.    */
  1302.   _setupBakerWindowControls() {
  1303.     const notablePostsTitle = `Only show, notables, nominators, q, q replied
  1304.       posts`;
  1305.  
  1306.     window.bakerTools.mainWindow.addOption(`
  1307.     <label for="${this.showOnlyNotablesCheckboxId}"
  1308.       title="${notablePostsTitle}" >
  1309.       Only Show Notable/Nomination Posts:
  1310.     </label>
  1311.     <input type="checkbox" id="${this.showOnlyNotablesCheckboxId}"
  1312.       title="${notablePostsTitle}" />
  1313.     `);
  1314.  
  1315.  
  1316.     window.bakerTools.mainWindow.addBaker(`
  1317.     <button type="button" id="${this.createNotablePostButtonId}"
  1318.       title="Create notables list post based on current nominated notables" >
  1319.       Create Notable Post
  1320.     </button>
  1321.     <textarea id="${this.notableEditorId}"></textarea>
  1322.     `);
  1323.   }
  1324.  
  1325.   /**
  1326.    * Setup listeners for new posts, bakerwindow controls, etc
  1327.    */
  1328.   _setupListeners() {
  1329.     $('#'+this.showOnlyNotablesCheckboxId).change(function(e) {
  1330.       this.setOnlyShowNotables(e.target.checked);
  1331.     }.bind(this));
  1332.  
  1333.     $('#'+this.createNotablePostButtonId).click(function() {
  1334.       if ($('#'+this.notableEditorId).val()) {
  1335.         if (!confirm(`If you continue, any changes you made will be
  1336.             overwritten!`)) {
  1337.           return;
  1338.         }
  1339.       }
  1340.       $('#'+this.notableEditorId).val(this.createNotablesPost());
  1341.     }.bind(this));
  1342.  
  1343.     $(document).on(ResearchBread.NEW_POST_EVENT, function(e, post) {
  1344.       this.checkNewPostsForNotables(post);
  1345.     }.bind(this));
  1346.   }
  1347.  
  1348.   /**
  1349.    * Create the notables post for review
  1350.    * @return {string} Returns the notable post string
  1351.    */
  1352.   createNotablesPost() {
  1353.     const notables = NotablePost.getNotables();
  1354.     const breadNumber = ResearchBread.getBreadNumber();
  1355.     let post = `'''#${breadNumber}'''\n\n`;
  1356.  
  1357.     notables.forEach(function(notable) {
  1358.       post += `${notable.shortLink()} ${notable.description}\n\n`;
  1359.     });
  1360.  
  1361.     return post;
  1362.   }
  1363.  
  1364.   /**
  1365.    * Checks a post for notable nominations
  1366.    * @param {Element} post
  1367.    */
  1368.   checkNewPostsForNotables(post) {
  1369.     $(post).removeAttr('style'); // TODO: try removing
  1370.  
  1371.     if (this.isNominatingPost(post)) {
  1372.       NotablePost.fromNominatingPost(post);
  1373.     }
  1374.   }
  1375.  
  1376.   /**
  1377.    * Finds posts that are being tagged as notable.
  1378.    *
  1379.    * I.E. Finding any post that has been replied to by a post with the string
  1380.    * "notable" in it. Maybe at somepoint this can be smarter.  Q give me some
  1381.    * dwave snow white tech!
  1382.    *
  1383.    * Highlights notable posts in yellow
  1384.    * Highlights nominating posts in pink <3
  1385.    * Highlights nominating posts in mentions
  1386.    * Add nominee count to post
  1387.    * @return {Array<NotablePost>}
  1388.    */
  1389.   findNominatedNotables() {
  1390.     const postsWithoutDough = ResearchBread.getPostsWithoutDough();
  1391.  
  1392.     // ^s to ignore notables review posts
  1393.     const nominatingPosts = postsWithoutDough
  1394.         .filter((post) => this.isNominatingPost(post));
  1395.  
  1396.     nominatingPosts.forEach(function(nominatingPost) {
  1397.       NotablePost.fromNominatingPost(nominatingPost);
  1398.     });
  1399.     console.log(NotablePost.getNotables());
  1400.     return NotablePost.getNotables();
  1401.   }
  1402.  
  1403.   /**
  1404.    * Is the post nominating a notable
  1405.    * @arg {Element} post .post
  1406.    * @return {boolean} True if post nominates a notable
  1407.    */
  1408.   isNominatingPost(post) {
  1409.     const postContainsNotable = post.textContent
  1410.         .search(this.NOMINATING_REGEX) != -1;
  1411.     const postIsReplying = post.querySelector('.body')
  1412.         .innerHTML
  1413.         .match(ResearchBread.REPLY_REGEX);
  1414.     return postContainsNotable && postIsReplying;
  1415.   }
  1416.  
  1417.   /**
  1418.    * Toggle whether only the notable/nominee posts are shown or not
  1419.    * @arg {boolean} onlyShowNotables boolean If true, only show
  1420.    *               notables/nominators, else show all
  1421.    */
  1422.   setOnlyShowNotables(onlyShowNotables) {
  1423.     $('#'+this.showOnlyNotablesCheckboxId).prop('checked', onlyShowNotables);
  1424.  
  1425.     localStorage.setItem(NotableHighlighter.ONLY_SHOW_NOTABLES_SETTING,
  1426.         onlyShowNotables);
  1427.  
  1428.     const notableOrNominationPostsSelector =
  1429.       `div.post.${NotableHighlighter.NOTABLE_CLASS},
  1430.       div.post.${NotableHighlighter.NOMINATOR_CLASS}`;
  1431.     const notableOrNominationPostBreaksSelector =
  1432.       `div.post.${NotableHighlighter.NOTABLE_CLASS}+br,
  1433.       div.post.${NotableHighlighter.NOMINATOR_CLASS}+br`;
  1434.     const onlyShowNotablesStyleId = 'bakertools-only-show-notables';
  1435.  
  1436.     if (onlyShowNotables) {
  1437.       $(`<style id='${onlyShowNotablesStyleId}' type='text/css'>
  1438.         div.reply:not(.post-hover),
  1439.         div.post+br {
  1440.           display: none !important;
  1441.           visibility: hidden !important;
  1442.         }
  1443.         ${notableOrNominationPostsSelector},
  1444.         ${notableOrNominationPostBreaksSelector} {
  1445.           display: inline-block !important;
  1446.           visibility: visible !important;
  1447.         }
  1448.         </style>`).appendTo('head');
  1449.     } else {
  1450.       $(`#${onlyShowNotablesStyleId}`).remove();
  1451.       // For whatever reason, when the non notable posts are filtered and new
  1452.       // posts come through the auto_update, the posts are created with
  1453.       // style="display:block" which messes up display.  Remove style attr
  1454.       // TODO: can we remove this now that we have !important?
  1455.       $(ResearchBread.POST_SELECTOR).removeAttr('style');
  1456.     }
  1457.   }
  1458.  
  1459.   /**
  1460.    * Retrieves only show notable ssetting from localStorage
  1461.    * @return {boolean} true if only show notables is turned on
  1462.    */
  1463.   getOnlyShowNotables() {
  1464.     return localStorage
  1465.         .getItem(NotableHighlighter.ONLY_SHOW_NOTABLES_SETTING);
  1466.   }
  1467. }
  1468. NotableHighlighter.NOMINATOR_CLASS = 'bakertools-notable-nominator';
  1469. NotableHighlighter.NOTABLE_CLASS = 'bakertools-notable';
  1470. NotableHighlighter.ONLY_SHOW_NOTABLES_SETTING =
  1471.     'bakertools-only-show-notables';
  1472.  
  1473.  
  1474.  
  1475. /* global $ */
  1476. /**
  1477. * Highlights previous bread post links
  1478. */
  1479. class PreviousBreadHighlighter {
  1480.   /**
  1481.    * Construct pb highlighter object, setup listeners
  1482.    */
  1483.   constructor() {
  1484.     this.previousBreadClass = 'bakertools-PreviousBread';
  1485.     this._linkSelector = 'div.body > p.body-line.ltr > a';
  1486.  
  1487.     this._setupStyles();
  1488.  
  1489.     const links = $(this._linkSelector).filter('[onClick]');
  1490.     links.each(function(index, link) {
  1491.       this.markLinkIfPreviousBread(link);
  1492.     }.bind(this));
  1493.  
  1494.     this._setupListeners();
  1495.   }
  1496.  
  1497.   /**
  1498.    * Setup styles for pb links
  1499.    */
  1500.   _setupStyles() {
  1501.     const sheet = window.document.styleSheets[0];
  1502.     sheet.insertRule(`div.post.reply div.body
  1503.       a.${this.previousBreadClass} {
  1504.         color: #8B0000;
  1505.     }`, sheet.cssRules.length);
  1506.     sheet.insertRule(`a.${this.previousBreadClass}::after {
  1507.       content: " (pb)";
  1508.     }`, sheet.cssRules.length);
  1509.   }
  1510.  
  1511.   /**
  1512.    * Setup listeners for pb highlighting
  1513.    */
  1514.   _setupListeners() {
  1515.     $(document).on(ResearchBread.NEW_POST_EVENT, function(e, post) {
  1516.       $(post).find(this._linkSelector)
  1517.           .each((index, link) => this.markLinkIfPreviousBread(link));
  1518.     }.bind(this));
  1519.   }
  1520.  
  1521.   /**
  1522.    * Marks the link if it is pb
  1523.    *
  1524.    * @param {Anchor} link
  1525.    */
  1526.   markLinkIfPreviousBread(link) {
  1527.     const breadFileName = document.location.pathname.split('/').slice(-1)[0];
  1528.     const linkFileName = link.href.split('/').slice(-1)[0].split('#')[0];
  1529.  
  1530.     if ($(link).attr('onclick').search(ResearchBread.REPLY_REGEX) !=1 &&
  1531.           breadFileName != linkFileName) {
  1532.       $(link).addClass(this.previousBreadClass);
  1533.     }
  1534.   }
  1535. }
  1536.  
  1537.  
  1538.  
  1539. /* global $, ResearchBread, NavigationControl */
  1540. /**
  1541. * Highlight Q posts, replies to q, q replies.
  1542. * Adds navigation to baker window
  1543. */
  1544. class QPostHighlighter {
  1545.   /**
  1546.    * Construct qposthighlighter object and setup listeners
  1547.    */
  1548.   constructor() {
  1549.     this.qPostClass = 'bakertools-q-post';
  1550.     this.qReplyClass = 'bakertools-q-reply';
  1551.     this.qMentionClass = 'bakertools-q-mention';
  1552.     this.qLinkClass = 'bakertools-q-link';
  1553.     this._linkSelector = 'div.body > p.body-line.ltr > a';
  1554.     this.currentQTripCode = null;
  1555.  
  1556.     this.showQNavigationInBoardListId =
  1557.         'bakertools-show-q-nav-in-boardlist';
  1558.  
  1559.     this._setupStyles();
  1560.     this._getCurrentQTripFromBread();
  1561.     this._findQPosts();
  1562.     this._setupBakerWindowControls();
  1563.     this._setupListeners();
  1564.     this._readSettings();
  1565.   }
  1566.  
  1567.   /**
  1568.    * Read settings from localStorage
  1569.    */
  1570.   _readSettings() {
  1571.     this.showQNavigationInBoardList(JSON.parse(
  1572.         localStorage
  1573.             .getItem(QPostHighlighter.SHOW_Q_NAV_IN_BOARDLIST_SETTING),
  1574.     ));
  1575.   }
  1576.  
  1577.   /**
  1578.    * Setup styles for highlighting q posts
  1579.    */
  1580.   _setupStyles() {
  1581.     const sheet = window.document.styleSheets[0];
  1582.     // Dark to light
  1583.     sheet.insertRule(`div.post.reply.${this.qPostClass} {
  1584.       background: #FFFFFF !important;
  1585.       display: inline-block !important;
  1586.       visibility: visible !important;
  1587.     }`, sheet.cssRules.length);
  1588.  
  1589.     // Enlightened by the Q but still not all the way
  1590.     sheet.insertRule(`div.post.reply.${this.qReplyClass} {
  1591.       background: #DDDDDD;
  1592.       display: inline-block !important;
  1593.       visibility: visible !important;
  1594.     }`, sheet.cssRules.length);
  1595.  
  1596.     // Trippy mentions
  1597.     sheet.insertRule(`div.post.reply .intro .${this.qMentionClass},
  1598.     .${this.qLinkClass}
  1599.     {
  1600.       padding:1px 3px 1px 3px;
  1601.       background-color:black;
  1602.       border-radius:8px;
  1603.       border:1px solid #bbbbee;
  1604.       color:gold;
  1605.       background: linear-gradient(300deg, #ff0000, #ff0000, #ff0000, #bbbbbb,
  1606.                 #4444ff);
  1607.       background-size: 800% 800%;
  1608.  
  1609.       -webkit-animation: Patriot 5s ease infinite;
  1610.       -moz-animation: Patriot 5s ease infinite;
  1611.       -o-animation: Patriot 5s ease infinite;
  1612.       animation: Patriot 5s ease infinite;
  1613.       -webkit-text-fill-color: transparent;
  1614.        
  1615.       background: -o-linear-gradient(transparent, transparent);
  1616.       -webkit-background-clip: text;
  1617.     }`, sheet.cssRules.length);
  1618.   }
  1619.  
  1620.   /**
  1621.    * Get Q's current trip code from the bread
  1622.    */
  1623.   _getCurrentQTripFromBread() {
  1624.     const tripCodeMatch = $('div.post.op')
  1625.         .text()
  1626.         .match(/Q's Trip-code: Q (.+?\s)/);
  1627.  
  1628.     if (!tripCodeMatch) {
  1629.       console.error('Could not find Q\'s tripcode');
  1630.       return;
  1631.     }
  1632.     this.currentQTripCode = tripCodeMatch[1].split(' ')[0];
  1633.   }
  1634.  
  1635.   /**
  1636.    * Find current Q posts in bread
  1637.    */
  1638.   _findQPosts() {
  1639.     const posts = ResearchBread.getPostsWithoutDough();
  1640.  
  1641.     $(posts).each(function(i, post) {
  1642.       this._doItQ(post);
  1643.     }.bind(this));
  1644.   }
  1645.  
  1646.   /**
  1647.    * Check if the post is Q
  1648.    * WWG1WGA
  1649.    *
  1650.    * @param {Element} post a div.post
  1651.    */
  1652.   _doItQ(post) {
  1653.     if (this._markIfQPost(post)) { // Q Post, lets check for q replies
  1654.       const qPostNumber = $(post)
  1655.           .find('.intro .post_no')
  1656.           .text()
  1657.           .replace('No.', '');
  1658.  
  1659.       const links = $(post)
  1660.           .find(this._linkSelector)
  1661.           .filter('[onClick]');
  1662.  
  1663.       $(links).each(function(i, link) {
  1664.         const postNumber = link.href.split('#')[1];
  1665.         // Enlightened post
  1666.         $(`#reply_${postNumber}`).addClass(this.qReplyClass);
  1667.  
  1668.         const metionLinkSelector = `#reply_${postNumber} .intro .mentioned a`;
  1669.         $(metionLinkSelector).each(function(i, mentionAnchor) {
  1670.           const mentionPostNumber = $(mentionAnchor).text().replace('>>', '');
  1671.           if (mentionPostNumber == qPostNumber) {
  1672.             $(mentionAnchor).addClass(this.qMentionClass);
  1673.           }
  1674.         }.bind(this));
  1675.       }.bind(this));
  1676.     } else { // Not Q, but lets check if this post replies to Q
  1677.       const links = $(post).find(this._linkSelector).filter('[onClick]');
  1678.  
  1679.       $(links).each(function(i, link) {
  1680.         const postNumber = link.href.split('#')[1];
  1681.         const replyPost = document.querySelector(`#reply_${postNumber}`);
  1682.         if (this._isQ(replyPost)) {
  1683.           $(link).addClass(this.qLinkClass);
  1684.         }
  1685.       }.bind(this));
  1686.     }
  1687.   }
  1688.  
  1689.   /**
  1690.    * @arg {Element} post div.post.reply
  1691.    * @return {boolean} true if it is a q post
  1692.    */
  1693.   _markIfQPost(post) {
  1694.     let isQ = false;
  1695.     if (this._isQ(post)) {
  1696.       $(post).addClass(this.qPostClass);
  1697.       QPostHighlighter.qPosts.push(post);
  1698.       isQ = true;
  1699.       $(document).trigger(QPostHighlighter.NEW_Q_POST_EVENT);
  1700.     }
  1701.     return isQ;
  1702.   }
  1703.  
  1704.   /**
  1705.    * Is the post Q?
  1706.    * @param {Element} post a div.post.reply
  1707.    * @return {boolean} true if the post is Q
  1708.    */
  1709.   _isQ(post) {
  1710.     return this._getTripCodeOfPost(post) == this.currentQTripCode;
  1711.   }
  1712.  
  1713.   /**
  1714.    * Get the trip code of the provided post
  1715.    * @param {Element} post
  1716.    * @return {string} tripcode
  1717.    */
  1718.   _getTripCodeOfPost(post) {
  1719.     return $(post).find('.intro span.trip').text();
  1720.   }
  1721.  
  1722.   /**
  1723.    * Add Q post navigation to bakerwindow
  1724.    */
  1725.   _setupBakerWindowControls() {
  1726.     window.bakerTools.mainWindow
  1727.         .addOption(`
  1728.     <br /><br />
  1729.     <label for="${this.showQNavigationInBoardListId}"
  1730.       title="Show navigation controls in board list bar" >
  1731.       Show Q Nav in Board List:
  1732.     </label>
  1733.     <input type="checkbox" id="${this.showQNavigationInBoardListId}"
  1734.       title="Show navigation controls in board list bar" /><br />
  1735.     `);
  1736.  
  1737.     this.navigation = new NavigationControl('Q Posts',
  1738.         () => QPostHighlighter.qPosts, 'new_q_post');
  1739.     window.bakerTools.mainWindow
  1740.         .addNavigation(this.navigation.element);
  1741.  
  1742.     // Boardlist nav starts out hidden
  1743.     // TODO: read from config
  1744.     this.boardListNav = new NavigationControl('Q Posts',
  1745.         () => QPostHighlighter.qPosts, 'new_q_post');
  1746.  
  1747.     $('.boardlist:first').append(this.boardListNav.element);
  1748.     $(this.boardListNav.element).hide();
  1749.   }
  1750.  
  1751.  
  1752.   /**
  1753.    * Setup listeners for new posts
  1754.    */
  1755.   _setupListeners() {
  1756.     $(document).on(ResearchBread.NEW_POST_EVENT, function(e, post) {
  1757.       this._doItQ(post);
  1758.     }.bind(this));
  1759.  
  1760.     $('#'+this.showQNavigationInBoardListId).change(function(e) {
  1761.       this.showQNavigationInBoardList(e.target.checked);
  1762.     }.bind(this));
  1763.   }
  1764.  
  1765.   /**
  1766.    * Show or hide q nav control in the boardlist
  1767.    *
  1768.    * @param {boolean} showNavInBoardList
  1769.    */
  1770.   showQNavigationInBoardList(showNavInBoardList) {
  1771.     $('#'+this.showQNavigationInBoardListId).prop('checked',
  1772.         showNavInBoardList);
  1773.  
  1774.     localStorage.setItem(QPostHighlighter.SHOW_Q_NAV_IN_BOARDLIST_SETTING,
  1775.         showNavInBoardList);
  1776.  
  1777.     if (showNavInBoardList) {
  1778.       $(this.boardListNav.element).show();
  1779.     } else {
  1780.       $(this.boardListNav.element).hide();
  1781.     }
  1782.   }
  1783. }
  1784. QPostHighlighter.qPosts = [];
  1785. QPostHighlighter.NEW_Q_POST_EVENT = 'bakertools-new-q-post';
  1786. QPostHighlighter.SHOW_Q_NAV_IN_BOARDLIST_SETTING =
  1787.     'bakertools-show-q-nav-in-boardlist';
  1788.  
  1789. /* global $, ResearchBread, QPostHighlighter, NotablePost */
  1790. /**
  1791. * Overlays bread stats (and some other controls) in the bottom right of the
  1792. * screen.
  1793. * TODO: create add method
  1794. */
  1795. class StatsOverlay {
  1796.   /**
  1797.    * Construct statsoverlay, html element, setup listeners
  1798.    */
  1799.   constructor() {
  1800.     this.id = 'bakertools-stats-overlay';
  1801.     this.maxPosts = 750;
  1802.     this.postCountId = 'bakertools-stats-post-count';
  1803.     this.userCountId = 'bakertools-stats-uid-count';
  1804.     this.qCountId = 'bakertools-stats-q-count';
  1805.     this.notableCountId = 'bakertools-stats-notable-count';
  1806.  
  1807.     this._createStyles();
  1808.     this._createElement();
  1809.     this._updateStats();
  1810.     $(document).on(ResearchBread.NEW_POST_EVENT, function(e, post) {
  1811.       this._updateStats();
  1812.     }.bind(this));
  1813.   }
  1814.  
  1815.   /**
  1816.    * Create styles for stats overlay
  1817.    */
  1818.   _createStyles() {
  1819.     const sheet = window.document.styleSheets[0];
  1820.     sheet.insertRule(`#${this.id} {
  1821.       padding: 5px;
  1822.       position: fixed;
  1823.       z-index: 100;
  1824.       float: right;
  1825.       right:28.25px;
  1826.       bottom: 28.25px;
  1827.     }`, sheet.cssRules.length);
  1828.   }
  1829.  
  1830.   /**
  1831.    * Create actual html element for style overlay
  1832.    */
  1833.   _createElement() {
  1834.     this.element = document.createElement('div');
  1835.     this.element.id = this.id;
  1836.  
  1837.     this.element.innerHTML = `
  1838.     Posts: <span id="${this.postCountId}" ></span>
  1839.     UIDS: <span id="${this.userCountId}"></span>
  1840.     <a href="#bottom" alt="to-bottom">
  1841.       <i class="fa fa-angle-double-down"></i>
  1842.     </a>
  1843.     <a href="#top" alt="to-top"><i class="fa fa-angle-double-up"></i></a><br/>
  1844.  
  1845.     Q's: <span id="${this.qCountId}" ></span>
  1846.    Notables: <span id="${this.notableCountId}"></span>
  1847.    `;
  1848.    document.body.appendChild(this.element);
  1849.  }
  1850.  
  1851.  /**
  1852.   * Update the stats fields
  1853.   */
  1854.  _updateStats() {
  1855.    const postCount = $('#thread_stats_posts').text();
  1856.    const progress = postCount/this.maxPosts;
  1857.  
  1858.    let postColor = 'green';
  1859.    if (progress >= .87) { // ~ 650 posts (100 posts left)
  1860.      postColor = 'red';
  1861.    } else if (progress >= .5) {
  1862.      postColor = 'goldenrod';
  1863.    }
  1864.    $('#'+this.postCountId).text(postCount).css({'color': postColor});
  1865.    $('#'+this.userCountId).text($('#thread_stats_uids').text());
  1866.    $('#'+this.qCountId).text(QPostHighlighter.qPosts.length);
  1867.    $('#'+this.notableCountId).text(NotablePost.getNotables().length);
  1868.  }
  1869. } // End StatsOverlay class
  1870.  
  1871. /* global $, NavigationControl, ResearchBread */
  1872. /**
  1873. * Highlight posts that (you)
  1874. * Adds (You) navigation links to baker window
  1875. */
  1876. class YouHighlighter {
  1877.  /**
  1878.   * Construct YN object
  1879.   */
  1880.  constructor() {
  1881.    this.showYouNavigationInBoardListId =
  1882.        'bakertools-show-you-nav-in-boardlist';
  1883.  
  1884.    this.showOwnNavigationInBoardListId =
  1885.        'bakertools-show-own-nav-in-boardlist';
  1886.    this._setupBakerWindowControls();
  1887.    this._setupListeners();
  1888.    this._readSettings();
  1889.  }
  1890.  
  1891.  /**
  1892.   * Read settings from localStorage
  1893.   */
  1894.  _readSettings() {
  1895.    this.showYouNavigationInBoardList(JSON.parse(
  1896.        localStorage.getItem(
  1897.            YouHighlighter.SHOW_YOU_NAV_IN_BOARDLIST_SETTING),
  1898.    ));
  1899.  
  1900.    this.showOwnNavigationInBoardList(JSON.parse(
  1901.        localStorage.getItem(
  1902.            YouHighlighter.SHOW_OWN_NAV_IN_BOARDLIST_SETTING),
  1903.    ));
  1904.  }
  1905.  
  1906.  /**
  1907.   * Add (you) navigation to bakerwindow
  1908.   */
  1909.  _setupBakerWindowControls() {
  1910.    const youLabel = `(You)'s`;
  1911.     this.youNavigation = new NavigationControl(youLabel,
  1912.         this._getYous, ResearchBread.NEW_POST_EVENT);
  1913.  
  1914.     // Boardlist nav starts out hidden
  1915.     // TODO: read from config
  1916.     this.youBoardListNav = new NavigationControl(youLabel,
  1917.         this._getYous, ResearchBread.NEW_POST_EVENT);
  1918.  
  1919.     $('.boardlist:first').append(this.youBoardListNav.element);
  1920.     $(this.youBoardListNav.element).hide();
  1921.  
  1922.     window.bakerTools.mainWindow.addOption(`
  1923.     <label for="${this.showYouNavigationInBoardListId}"
  1924.       title="Show navigation controls in board list bar" >
  1925.       Show (You) Nav in Board List:
  1926.     </label>
  1927.     <input type="checkbox" id="${this.showYouNavigationInBoardListId}"
  1928.       title="Show navigation controls in board list bar" /><br />
  1929.     `);
  1930.  
  1931.     const ownLabel = `Own Posts`;
  1932.     this.ownNavigation = new NavigationControl(ownLabel,
  1933.         this._getOwnPosts, ResearchBread.NEW_POST_EVENT);
  1934.  
  1935.     // Boardlist nav starts out hidden
  1936.     // TODO: read from config
  1937.     this.ownBoardListNav = new NavigationControl(ownLabel,
  1938.         this._getOwnPosts, ResearchBread.NEW_POST_EVENT);
  1939.  
  1940.     $('.boardlist:first').append(this.ownBoardListNav.element);
  1941.     $(this.ownBoardListNav.element).hide();
  1942.  
  1943.     window.bakerTools.mainWindow.addOption(`
  1944.     <label for="${this.showOwnNavigationInBoardListId}"
  1945.       title="Show navigation controls in board list bar" >
  1946.       Show Own Post Nav in Board List:
  1947.     </label>
  1948.     <input type="checkbox" id="${this.showOwnNavigationInBoardListId}"
  1949.       title="Show navigation controls in board list bar" /><br />
  1950.     `);
  1951.  
  1952.     window.bakerTools.mainWindow.addNavigation(this.youNavigation.element);
  1953.     window.bakerTools.mainWindow.addNavigation(this.ownNavigation.element);
  1954.   }
  1955.  
  1956.   /**
  1957.    * Setup listeners for baker window controls
  1958.    */
  1959.   _setupListeners() {
  1960.     $('#'+this.showOwnNavigationInBoardListId).change(function(e) {
  1961.       this.showOwnNavigationInBoardList(e.target.checked);
  1962.     }.bind(this));
  1963.  
  1964.     $('#'+this.showYouNavigationInBoardListId).change(function(e) {
  1965.       this.showYouNavigationInBoardList(e.target.checked);
  1966.     }.bind(this));
  1967.   }
  1968.  
  1969.   /**
  1970.    * Show/hide you nav in boardlist
  1971.    *
  1972.    * @param {boolean} showYouNavInBoardList
  1973.    */
  1974.   showYouNavigationInBoardList(showYouNavInBoardList) {
  1975.     $('#'+this.showYouNavigationInBoardListId).prop('checked',
  1976.         showYouNavInBoardList);
  1977.  
  1978.     localStorage.setItem(YouHighlighter.SHOW_YOU_NAV_IN_BOARDLIST_SETTING,
  1979.         showYouNavInBoardList);
  1980.  
  1981.     if (showYouNavInBoardList) {
  1982.       $(this.youBoardListNav.element).show();
  1983.     } else {
  1984.       $(this.youBoardListNav.element).hide();
  1985.     }
  1986.   }
  1987.  
  1988.   /**
  1989.    * Show/hide own nav in boardlist
  1990.    *
  1991.    * @param {boolean} showOwnNavInBoardList
  1992.    */
  1993.   showOwnNavigationInBoardList(showOwnNavInBoardList) {
  1994.     $('#'+this.showOwnNavigationInBoardListId).prop('checked',
  1995.         showOwnNavInBoardList);
  1996.  
  1997.     localStorage.setItem(YouHighlighter.SHOW_OWN_NAV_IN_BOARDLIST_SETTING,
  1998.         showOwnNavInBoardList);
  1999.  
  2000.     if (showOwnNavInBoardList) {
  2001.       $(this.ownBoardListNav.element).show();
  2002.     } else {
  2003.       $(this.ownBoardListNav.element).hide();
  2004.     }
  2005.   }
  2006.  
  2007.   /**
  2008.    * Get (You)'s
  2009.    * @return {JQuery}
  2010.    */
  2011.   _getYous() {
  2012.     return $('div.post.reply').filter(function(idx, post) {
  2013.       return post.querySelector('.body')
  2014.           .innerHTML
  2015.           .indexOf('<small>(You)</small>') != -1;
  2016.     });
  2017.   }
  2018.  
  2019.   /**
  2020.    * Get own posts
  2021.    * @return {JQuery}
  2022.    */
  2023.   _getOwnPosts() {
  2024.     return $('div.post.you');
  2025.   }
  2026. }
  2027. YouHighlighter.SHOW_YOU_NAV_IN_BOARDLIST_SETTING =
  2028.     'bakertools-show-you-nav-in-boardlist';
  2029. YouHighlighter.SHOW_OWN_NAV_IN_BOARDLIST_SETTING =
  2030.     'bakertools-show-own-nav-in-boardlist';
  2031.  
  2032.  
  2033. /* global ActivePage, $, QPostHighlighter, YouHighlighter, StatsOverlay,
  2034.  NotableHighlighter, BakerWindow, BlurImages, PreviousBreadHighlighter,
  2035.  NominatePostButtons, BreadList */
  2036. /**
  2037. * MAIN
  2038. */
  2039. if (ActivePage.isThread()) { // Only setup the tools if we are on a thread
  2040.   $(document).ready(function() {
  2041.     window.bakerTools = {};
  2042.     window.bakerTools.mainWindow = new BakerWindow();
  2043.     new BlurImages();
  2044.     window.bakerTools.notableHighlighter = new NotableHighlighter();
  2045.     window.bakerTools.PreviousBreadHighlighter =
  2046.       new PreviousBreadHighlighter();
  2047.     window.bakerTools.qPostHighlighter = new QPostHighlighter();
  2048.     window.bakerTools.youHighlighter = new YouHighlighter();
  2049.     window.bakerTools.statsOverlay = new StatsOverlay();
  2050.     new NominatePostButtons();
  2051.     new BreadList();
  2052.   });
  2053. }
  2054. }(window.jQuery));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement