Advertisement
Guest User

8kun Baker tools v0.5.2

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