Advertisement
Guest User

8kun Baker tools v0.4.0

a guest
Dec 31st, 2019
1,261
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.    ==8Kun Baker Tools==
  3.    Version 0.4.0
  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.   * NEW IN 0.4.0: Adds "Notable Nomination" button to posts that opens the
  14.     Quick Reply box and prefills it with a BAKER NOTABLE Template
  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.     * NEW IN 0.4.0: Option to blur images until hover
  30.     * NEW IN 0.4.0: Cycle through (You)'s
  31.     * NEW IN 0.4.0: 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.4.0'''
  43.   * Option to blur images until hover
  44.   * Adds a "Notable Nomination" button to posts that opens the Quick Reply
  45.     box and prefills it with a BAKER NOTABLE Template
  46.   * Add Q Post navigation links to the Baker Window
  47.   * Add (You) navigation links to the Baker Window
  48.   * Add own post navigation links to the Baker Window
  49.   * Cleaned up baker window design
  50.  
  51.   * More code cleanup and linting changes
  52.  
  53.   '''0.3.0'''
  54.   * Highlights Q Posts with white BG -> DARK TO LIGHT!
  55.   * Highlights Q posts in mentions (I.E. posts that get (YOU)'ed)
  56.   * Highlights links to Q Posts
  57.  
  58.   * Refactored code into classes for easier maint.
  59.  
  60.   '''0.2.0'''
  61.   * Highlight pb links
  62.   * Thread stats overlay with
  63.       * color coded reply count that goes from green to red as bread ages
  64.       * UID Count
  65.       * Jump To Bottom Link
  66.       * Jump To Bottom Top link
  67.  
  68.   '''0.1.0'''
  69.   Initial release:
  70.   * Highlight notables and nominators
  71.   * Filter to only show notables and nominators
  72.   * Create notables post
  73.  
  74.   Version History:
  75.   https://pastebin.com/kz9LrcE9 0.3.0
  76.   https://pastebin.com/4aEFsPwK 0.2.0
  77.   https://pastebin.com/eNmTtzdi 0.1.0
  78.  
  79. TODO: false positive notables and nominations
  80. TODO: Bread list
  81. TODO: Get notable description of pb notables
  82. TODO: save/Load settings (like for bakerwindow selections)
  83. TODO: Create Dough?
  84. TODO: Flood detect
  85. */
  86. (function($) {
  87.   'use strict';
  88.  
  89.   /**
  90.    * Wrapper for 8kun active_page variable to determine the type of
  91.    * page the user is on.
  92.    */
  93.   class ActivePage {
  94.     /**
  95.      * Are we currently on the thread index page?
  96.      * @return {boolean} True if on index
  97.      */
  98.     static isIndex() {
  99.       return window.active_page == ActivePage.Index;
  100.     }
  101.  
  102.     /**
  103.      * Are we currently on the thread catalog page?
  104.      * @return {boolean} True if on catalog
  105.      */
  106.     static isCatalog() {
  107.       return window.active_page == ActivePage.Catalog;
  108.     }
  109.  
  110.     /**
  111.      * Are we on a thread page?
  112.      * @return {boolean} True if on thread
  113.      */
  114.     static isThread() {
  115.       console.log(window.active_page == ActivePage.Thread);
  116.       return window.active_page == ActivePage.Thread;
  117.     }
  118.   }
  119.   ActivePage.Index = 'index';
  120.   ActivePage.Catalog = 'catalog';
  121.   ActivePage.Thread = 'thread';
  122.  
  123.   /**
  124.    * Creates first, prev, next, last navigation controls
  125.    */
  126.   class NavigationControl {
  127.     /**
  128.      * Construct navigatio control manager object
  129.      *
  130.      * @param {string} label the label for the control
  131.      * @param {Function} updateFunction Called to get latest data
  132.      * @param {string} updateEventName Called to get latest data
  133.      */
  134.     constructor(label, updateFunction, updateEventName) {
  135.       this.navigationControlClass = 'bakertools-navigation-control';
  136.  
  137.       const strippedName = label.replace(/(\s|\(|\)|'|"|:)/g, '');
  138.       this.label = label;
  139.       this.list = updateFunction();
  140.       $(document).on(updateEventName, () => this.list = updateFunction());
  141.  
  142.       this.currentIndex = -1;
  143.       this.navigationId = `bakertools-navcontrol-${strippedName}`;
  144.       this.currentIndexId = `${this.navigationId}-current-index`;
  145.       this.totalId = `${this.navigationId}-total`;
  146.       this.goToFirstId = `${this.navigationId}-goto-first`;
  147.       this.goToPreviousId = `${this.navigationId}-goto-prev`;
  148.       this.goToNextId = `${this.navigationId}-goto-next`;
  149.       this.goToLastId = `${this.navigationId}-goto-last`;
  150.  
  151.       this._createElement(label);
  152.       this._setupListeners();
  153.     }
  154.  
  155.     /**
  156.      * Create nav element
  157.      */
  158.     _createElement() {
  159.       this.element = $(`
  160.       <span title="Navigate ${this.label}">
  161.         <label for="${this.navigationId}">${this.label}:</label>
  162.         <span id="${this.navigationId}"
  163.           class="${this.navigationControlClass}">
  164.  
  165.           <i class="fa fa-fast-backward" id="${this.goToFirstId}"></i>
  166.           <i class="fa fa-backward" id="${this.goToPreviousId}"></i>
  167.  
  168.           <span id="${this.currentIndexId}">
  169.             ${this.currentIndex+1}
  170.           </span>
  171.           of
  172.           <span id="${this.totalId}">${this.list.length}</span>
  173.  
  174.           <i class="fa fa-forward" id="${this.goToNextId}"></i>
  175.           <i class="fa fa-fast-forward" id="${this.goToLastId}"></i>
  176.         </span>
  177.       </span>
  178.       <br />
  179.       `);
  180.     }
  181.  
  182.     /**
  183.      * Setup button event listeners
  184.      */
  185.     _setupListeners() {
  186.       this.element.find('#'+this.goToFirstId).click(function(e) {
  187.         this.goToFirstPost();
  188.       }.bind(this));
  189.  
  190.       this.element.find('#'+this.goToPreviousId).click(function(e) {
  191.         this.goToPreviousPost();
  192.       }.bind(this));
  193.  
  194.       this.element.find('#'+this.goToNextId).click(function(e) {
  195.         this.goToNextPost();
  196.       }.bind(this));
  197.  
  198.       this.element.find('#'+this.goToLastId).click(function(e) {
  199.         this.goToLastPost();
  200.       }.bind(this));
  201.     }
  202.  
  203.     /**
  204.      * Scroll to first post
  205.      */
  206.     goToFirstPost() {
  207.       console.info(`Go to first ${this.label} post`);
  208.       if (!this.list.length) {
  209.         return;
  210.       }
  211.       this.currentIndex = 0;
  212.       this.scrollToCurrentPost();
  213.     }
  214.  
  215.     /**
  216.      * Scroll to next navigated post
  217.      */
  218.     goToPreviousPost() {
  219.       console.info(`Go to prev ${this.label} post`);
  220.       if (!this.list.length) {
  221.         return;
  222.       }
  223.       if (this.currentIndex == 0) {
  224.         this.currentIndex = this.list.length - 1;
  225.       } else {
  226.         this.currentIndex--;
  227.       }
  228.       this.scrollToCurrentPost();
  229.     }
  230.     /**
  231.      * Scroll to next navigated post
  232.      */
  233.     goToNextPost() {
  234.       console.info(`Go to next ${this.label} post`);
  235.       if (!this.list.length) {
  236.         return;
  237.       }
  238.       const lastPostIndex = this.list.length - 1;
  239.       if (this.currentIndex == lastPostIndex) {
  240.         this.currentIndex = 0;
  241.       } else {
  242.         this.currentIndex++;
  243.       }
  244.       this.scrollToCurrentPost();
  245.     }
  246.  
  247.     /**
  248.      * Scroll the last  post in this bread into view
  249.      */
  250.     goToLastPost() {
  251.       console.info(`Go to last ${this.label} post`);
  252.       if (!this.list.length) {
  253.         return;
  254.       }
  255.       const numPosts = this.list.length;
  256.       this.currentIndex = numPosts - 1;
  257.       this.scrollToCurrentPost();
  258.     }
  259.  
  260.     /**
  261.      * Scrolls the current selected  post into view
  262.      */
  263.     scrollToCurrentPost() {
  264.       const post = this.list[this.currentIndex];
  265.       $(post).get(0).scrollIntoView();
  266.       $('#'+this.currentIndexId).text(this.currentIndex+1);
  267.       window.scrollBy(0, -20);
  268.     }
  269.   }
  270.  
  271.   /**
  272.    * Research Bread Class
  273.    */
  274.   class ResearchBread {
  275.     /**
  276.      * Get an array of post bodies with dough posts filtered out
  277.      * @return {NodeList} of .post elements
  278.      */
  279.     static getPostsWithoutDough() {
  280.       const posts = Array.from(document
  281.           .querySelectorAll(ResearchBread.POST_SELECTOR));
  282.  
  283.       const filteredPosts = posts.filter(function(post) {
  284.         return !post.querySelector('.body')
  285.             .innerText.match(ResearchBread.DOUGH_POSTS_REGEX);
  286.       });
  287.  
  288.       return filteredPosts;
  289.     }
  290.  
  291.     /**
  292.      * Determine what the bread number is
  293.      * @return {number} the number of the research bread
  294.      */
  295.     static getBreadNumber() {
  296.       const breadNumberRegex = /#(.+?) /;
  297.       return document.querySelector(ResearchBread.OP_SUBJECT_SELECTOR)
  298.           .innerText
  299.           .match(breadNumberRegex)[1] || 'COULD NOT FIND BREAD NUMBER';
  300.     }
  301.  
  302.     /**
  303.      * @arg {Element} post .post
  304.      * @return {RegexMatch} Capture 1 is the number
  305.      */
  306.     static getFirstReplyLink(post) {
  307.       const match = post.querySelector('.body')
  308.           .innerHTML
  309.           .match(ResearchBread.REPLY_REGEX);
  310.       return match && match[1] || null;
  311.     }
  312.   }
  313.   ResearchBread.OP_SUBJECT_SELECTOR = '.post.op > p > label > span.subject';
  314.   ResearchBread.POST_BODY_SELECTOR = '.post > .body';
  315.   ResearchBread.POST_SELECTOR = '.post';
  316.   ResearchBread.REPLY_REGEX = /highlightReply\('(.+?)'/;
  317.   ResearchBread.DOUGH_POSTS_REGEX = new RegExp(
  318.       `^(Welcome To Q Research General|` +
  319.       `Global Announcements|` +
  320.       `War Room` +
  321.       `|QPosts Archives).*`);
  322.  
  323.   /**
  324.   * Baker Window
  325.   */
  326.   class BakerWindow {
  327.     /**
  328.      * Construct Baker window element, register listeners
  329.      */
  330.     constructor() {
  331.       this.bakerWindowBodyId = 'bakertools-window-body';
  332.       this.bakerWindowHeaderId = 'bakertools-window-header';
  333.       this.bakerWindowOptionsId = 'bakertools-window-options';
  334.       this.bakerWindowNavigationId = 'bakertools-window-navigation';
  335.       this.bakerWindowBakerId = 'bakertools-window-baker';
  336.       this.bakerWindowCloseId = 'bakertools-window-close';
  337.       this.navigationControlClass = 'bakertools-navigation-control';
  338.       this.id = 'baker-tools-window';
  339.       this.element = null;
  340.  
  341.       this._createStyles();
  342.       this._createElement();
  343.       this._setupListeners();
  344.       this._setupBakerWindowLink();
  345.     }
  346.  
  347.     /**
  348.      * Create CSS styles needed by the window
  349.      */
  350.     _createStyles() {
  351.       const sheet = window.document.styleSheets[0];
  352.  
  353.       // Main window style
  354.       sheet.insertRule(`#${this.id} {
  355.           width: 300px;
  356.           background-color: rgb(214, 218, 240);
  357.           position: fixed;
  358.           z-index: 100;
  359.           float: right;
  360.           right:28.25px;
  361.           border: 1px solid;
  362.       }`, sheet.cssRules.length);
  363.  
  364.       // Window header headlines
  365.       sheet.insertRule(`#${this.id} #${this.bakerWindowHeaderId} {
  366.         background: #98E;
  367.         border: solid 1px;
  368.         text-align: center;
  369.         margin: 0px;
  370.       }`, sheet.cssRules.length);
  371.  
  372.       // Window header headlines
  373.       sheet.insertRule(`#${this.id} #${this.bakerWindowHeaderId} h3 {
  374.         margin: 0;
  375.       }`, sheet.cssRules.length);
  376.  
  377.       // Window close button
  378.       sheet.insertRule(`#${this.id} #${this.bakerWindowCloseId} {
  379.         top: 0px;
  380.         right: 0px;
  381.         position: absolute;
  382.         margin-right: 3px;
  383.         font-size: 20px;
  384.       }`, sheet.cssRules.length);
  385.  
  386.       // Window body
  387.       sheet.insertRule(`#${this.id} #${this.bakerWindowBodyId} {  
  388.         padding: 5px;
  389.       }`, sheet.cssRules.length);
  390.  
  391.  
  392.       sheet.insertRule(`#${this.id} details {
  393.         padding: 5px;
  394.       }`, sheet.cssRules.length);
  395.  
  396.       sheet.insertRule(`#${this.id} summary {
  397.             margin: 0 0 8px;
  398.             font-weight: bold;
  399.             border-bottom: solid 2px;
  400.       }`, sheet.cssRules.length);
  401.  
  402.       sheet.insertRule(`#${this.id} #${this.bakerWindowNavigationId}
  403.       .${this.navigationControlClass} {
  404.         float: right;
  405.       }`, sheet.cssRules.length);
  406.     }
  407.  
  408.     /**
  409.      * Create the actual window HTML element
  410.      */
  411.     _createElement() {
  412.       this.element = document.createElement('div');
  413.       this.element.id = this.id;
  414.  
  415.       this.element.innerHTML = `
  416.       <header id="${this.bakerWindowHeaderId}">
  417.         <h3>Baker Tools</h3>
  418.         <a id="${this.bakerWindowCloseId}" href="javascript:void(0)">
  419.           <i class="fa fa-times"></i>
  420.         </a>
  421.       </header>
  422.       <form id="${this.bakerWindowBodyId}">
  423.         <details id='${this.bakerWindowOptionsId}' open>
  424.           <summary>Options</summary>
  425.         </details>
  426.         <details id='${this.bakerWindowNavigationId}' open>
  427.           <summary>Navigation</summary>
  428.         </details>
  429.         <details id='${this.bakerWindowBakerId}' open>
  430.           <summary>Baker Tools</summary>
  431.         </details>
  432.       </form>
  433.       `;
  434.       document.body.appendChild(this.element);
  435.  
  436.       $(this.element).draggable();
  437.       $(this.element).hide();
  438.     }
  439.  
  440.     /**
  441.      * Setup event listeners for bakerwindow
  442.      */
  443.     _setupListeners() {
  444.       $('#'+this.bakerWindowCloseId).click(function(e) {
  445.         this.hide();
  446.       }.bind(this));
  447.     }
  448.  
  449.     /**
  450.      * Create link for show/hiding baker window, placed in boardlist bar
  451.      */
  452.     _setupBakerWindowLink() {
  453.       this.bakerToolsLink = document.createElement('a');
  454.       this.bakerToolsLink.textContent = '[Baker Tools]';
  455.       this.bakerToolsLink.style.cssText = 'float: right;';
  456.       this.bakerToolsLink.title = 'Baker Tools';
  457.       this.bakerToolsLink.href = 'javascript:void(0)';
  458.       document.querySelector('.boardlist').appendChild(this.bakerToolsLink);
  459.  
  460.       this.bakerToolsLink.onclick = this.toggle.bind(this);
  461.     }
  462.  
  463.     /**
  464.      * Show the baker window
  465.      */
  466.     show() {
  467.       $(this.element).css({'top': 15});
  468.       $(this.element).show();
  469.     }
  470.  
  471.     /**
  472.      * Hide the baker window
  473.      */
  474.     hide() {
  475.       $(this.element).hide();
  476.     }
  477.  
  478.     /**
  479.      * Is the baker window visible?
  480.      * @return {boolean} true if window is visible
  481.      */
  482.     isVisible() {
  483.       return $(this.element).is(':visible');
  484.     }
  485.  
  486.     /**
  487.      * Toggle visibility of baker window
  488.      */
  489.     toggle() {
  490.       if (this.isVisible()) {
  491.         this.hide();
  492.       } else {
  493.         this.show();
  494.       }
  495.     }
  496.  
  497.     /**
  498.      * Add form controls to options section of baker window
  499.      * @arg {Element} htmlContentString form controls
  500.      */
  501.     addOption(htmlContentString) {
  502.       $('#'+this.bakerWindowOptionsId).append(htmlContentString);
  503.     }
  504.  
  505.     /**
  506.      * Add html elements to the navigation section of the baker window
  507.      * @arg {Element} htmlContentString form controls
  508.      */
  509.     addNavigation(htmlContentString) {
  510.       $('#'+this.bakerWindowNavigationId).append(htmlContentString);
  511.     }
  512.  
  513.     /**
  514.      * Add html elements to the baker section of the baker window
  515.      * @arg {Element} htmlContentString form controls
  516.      */
  517.     addBaker(htmlContentString) {
  518.       $('#'+this.bakerWindowBakerId).append(htmlContentString);
  519.     }
  520.   } // end class BakerWindow
  521.  
  522.   /**
  523.    * Overlays bread stats (and some other controls) in the bottom right of the
  524.    * screen.
  525.    * TODO: create add method
  526.    */
  527.   class StatsOverlay {
  528.     /**
  529.      * Construct statsoverlay, html element, setup listeners
  530.      */
  531.     constructor() {
  532.       this.id = 'bakertools-stats-overlay';
  533.       this.maxPosts = 750;
  534.       this.postCountId = 'bakertools-stats-post-count';
  535.       this.userCountId = 'bakertools-stats-uid-count';
  536.       this.qCountId = 'bakertools-stats-q-count';
  537.       this.notableCountId = 'bakertools-stats-notable-count';
  538.  
  539.       this._createStyles();
  540.       this._createElement();
  541.       this._updateStats();
  542.       $(document).on('new_post', function(e, post) {
  543.         this._updateStats();
  544.       }.bind(this));
  545.     }
  546.  
  547.     /**
  548.      * Create styles for stats overlay
  549.      */
  550.     _createStyles() {
  551.       const sheet = window.document.styleSheets[0];
  552.       sheet.insertRule(`#${this.id} {
  553.         padding: 5px;
  554.         position: fixed;
  555.         z-index: 100;
  556.         float: right;
  557.         right:28.25px;
  558.         bottom: 28.25px;
  559.       }`, sheet.cssRules.length);
  560.     }
  561.  
  562.     /**
  563.      * Create actual html element for style overlay
  564.      */
  565.     _createElement() {
  566.       this.element = document.createElement('div');
  567.       this.element.id = this.id;
  568.  
  569.       this.element.innerHTML = `
  570.       Posts: <span id="${this.postCountId}" ></span>
  571.       UIDS: <span id="${this.userCountId}"></span>
  572.       <a href="#bottom" alt="to-bottom">
  573.         <i class="fa fa-angle-double-down"></i>
  574.       </a>
  575.       <a href="#top" alt="to-top"><i class="fa fa-angle-double-up"></i></a><br/>
  576.  
  577.       Q's: <span id="${this.qCountId}" ></span>
  578.      Notables: <span id="${this.notableCountId}"></span>
  579.      `;
  580.      document.body.appendChild(this.element);
  581.    }
  582.  
  583.    /**
  584.     * Update the stats fields
  585.     */
  586.    _updateStats() {
  587.      const postCount = $('#thread_stats_posts').text();
  588.      const progress = postCount/this.maxPosts;
  589.  
  590.      let postColor = 'green';
  591.      if (progress >= .87) { // ~ 650 posts (100 posts left)
  592.        postColor = 'red';
  593.      } else if (progress >= .5) {
  594.        postColor = 'goldenrod';
  595.      }
  596.      $('#'+this.postCountId).text(postCount).css({'color': postColor});
  597.      $('#'+this.userCountId).text($('#thread_stats_uids').text());
  598.      $('#'+this.qCountId).text(QPostHighlighter.qPosts.length);
  599.      $('#'+this.notableCountId).text(NotablePost.getNotables().length);
  600.    }
  601.  } // End StatsOverlay class
  602.  
  603.  /**
  604.   * Makes notable posts easier to see by highlighting posts that anons nominate
  605.   * as notable.
  606.   *
  607.   * If someone replies to a post and their post contains the word 'notable',
  608.   * the replied to post will be considered notable.
  609.   *
  610.   * Both the notable post and the nominator posts will be highlighted, as well
  611.   * as the nominator link in the notable's mentions will be highlighted.
  612.    */
  613.   class NotableHighlighter {
  614.     /**
  615.      * Construct notablehighlighter object, find and highlight
  616.      * current notable sand setup listeners
  617.      */
  618.     constructor() {
  619.       this.NOMINATING_REGEX = /notable/i;
  620.  
  621.       this.showOnlyNotablesCheckboxId = 'bakertools-show-only-notable';
  622.       this.createNotablePostButtonId = 'bakertools-create-notable-post';
  623.       this.notableEditorId = 'bakertools-notable-editor';
  624.  
  625.       this._createStyles();
  626.       this._setupBakerWindowControls();
  627.       this.findNominatedNotables();
  628.       this._setupListeners();
  629.     }
  630.  
  631.     /**
  632.      * Create styles that determine how notables are highlighted
  633.      */
  634.     _createStyles() {
  635.       const sheet = window.document.styleSheets[0];
  636.       sheet.insertRule(`div.post.${NotableHighlighter.NOTABLE_CLASS} {
  637.         background-color: #FFFFCC;
  638.       }`, sheet.cssRules.length);
  639.       sheet.insertRule(`div.post.${NotableHighlighter.NOMINATOR_CLASS} {
  640.         background-color: #FFCCE5;  
  641.       }`, sheet.cssRules.length);
  642.       sheet.insertRule(`
  643.       div.post.reply .mentioned .${NotableHighlighter.NOMINATOR_CLASS} {
  644.         color: #00CC00;
  645.         font-weight: bold;
  646.         font-size: 1.5em;
  647.       }`, sheet.cssRules.length);
  648.     }
  649.  
  650.     /**
  651.      * Add controls to the bakerwindow
  652.      */
  653.     _setupBakerWindowControls() {
  654.       const notablePostsTitle = `Only show, notables, nominators, q, q replied
  655.         posts`;
  656.  
  657.       window.bakerTools.mainWindow.addOption(`
  658.       <label for="${this.showOnlyNotablesCheckboxId}"
  659.         title="${notablePostsTitle}" >
  660.         Only Show Notable/Nomination Posts:
  661.       </label>
  662.       <input type="checkbox" id="${this.showOnlyNotablesCheckboxId}"
  663.         title="${notablePostsTitle}" />
  664.       `);
  665.  
  666.  
  667.       window.bakerTools.mainWindow.addBaker(`
  668.       <button type="button" id="${this.createNotablePostButtonId}"
  669.         title="Create notables list post based on current nominated notables" >
  670.         Create Notable Post
  671.       </button>
  672.       <textarea id="${this.notableEditorId}"></textarea>
  673.       `);
  674.     }
  675.  
  676.     /**
  677.      * Setup listeners for new posts, bakerwindow controls, etc
  678.      */
  679.     _setupListeners() {
  680.       $('#'+this.showOnlyNotablesCheckboxId).change(function(e) {
  681.         this.setOnlyShowNotables(e.target.checked);
  682.       }.bind(this));
  683.  
  684.       $('#'+this.createNotablePostButtonId).click(function() {
  685.         if ($('#'+this.notableEditorId).val()) {
  686.           if (!confirm(`If you continue, any changes you made will be
  687.               overwritten!`)) {
  688.             return;
  689.           }
  690.         }
  691.         $('#'+this.notableEditorId).val(this.createNotablesPost());
  692.       }.bind(this));
  693.  
  694.       $(document).on('new_post', function(e, post) {
  695.         this.checkNewPostsForNotables(post);
  696.       }.bind(this));
  697.     }
  698.  
  699.     /**
  700.      * Create the notables post for review
  701.      * @return {string} Returns the notable post string
  702.      */
  703.     createNotablesPost() {
  704.       const notables = NotablePost.getNotables();
  705.       const breadNumber = ResearchBread.getBreadNumber();
  706.       let post = `'''#${breadNumber}'''\n\n`;
  707.  
  708.       notables.forEach(function(notable) {
  709.         post += `${notable.shortLink()} ${notable.description}\n\n`;
  710.       });
  711.  
  712.       return post;
  713.     }
  714.  
  715.     /**
  716.      * Checks a post for notable nominations
  717.      * @param {Element} post
  718.      */
  719.     checkNewPostsForNotables(post) {
  720.       $(post).removeAttr('style'); // TODO: try removing
  721.  
  722.       if (this.isNominatingPost(post)) {
  723.         NotablePost.fromNominatingPost(post);
  724.       }
  725.     }
  726.  
  727.     /**
  728.      * Finds posts that are being tagged as notable.
  729.      *
  730.      * I.E. Finding any post that has been replied to by a post with the string
  731.      * "notable" in it. Maybe at somepoint this can be smarter.  Q give me some
  732.      * dwave snow white tech!
  733.      *
  734.      * Highlights notable posts in yellow
  735.      * Highlights nominating posts in pink <3
  736.      * Highlights nominating posts in mentions
  737.      * Add nominee count to post
  738.      * @return {Array<NotablePost>}
  739.      */
  740.     findNominatedNotables() {
  741.       const postsWithoutDough = ResearchBread.getPostsWithoutDough();
  742.  
  743.       // ^s to ignore notables review posts
  744.       const nominatingPosts = postsWithoutDough
  745.           .filter((post) => this.isNominatingPost(post));
  746.  
  747.       nominatingPosts.forEach(function(nominatingPost) {
  748.         NotablePost.fromNominatingPost(nominatingPost);
  749.       });
  750.       console.log(NotablePost.getNotables());
  751.       return NotablePost.getNotables();
  752.     }
  753.  
  754.     /**
  755.      * Is the post nominating a notable
  756.      * @arg {Element} post .post
  757.      * @return {boolean} True if post nominates a notable
  758.      */
  759.     isNominatingPost(post) {
  760.       const postContainsNotable = post.textContent
  761.           .search(this.NOMINATING_REGEX) != -1;
  762.       const postIsReplying = post.querySelector('.body')
  763.           .innerHTML
  764.           .match(ResearchBread.REPLY_REGEX);
  765.       return postContainsNotable && postIsReplying;
  766.     }
  767.  
  768.     /**
  769.      * Toggle whether only the notable/nominee posts are shown or not
  770.      * @arg {boolean} onlyShowNotables boolean If true, only show
  771.      *               notables/nominators, else show all
  772.      */
  773.     setOnlyShowNotables(onlyShowNotables) {
  774.       const notableOrNominationPostsSelector =
  775.         `div.post.${NotableHighlighter.NOTABLE_CLASS},
  776.         div.post.${NotableHighlighter.NOMINATOR_CLASS}`;
  777.       const notableOrNominationPostBreaksSelector =
  778.         `div.post.${NotableHighlighter.NOTABLE_CLASS}+br,
  779.         div.post.${NotableHighlighter.NOMINATOR_CLASS}+br`;
  780.       const onlyShowNotablesStyleId = 'bakertools-only-show-notables';
  781.  
  782.       if (onlyShowNotables) {
  783.         $(`<style id='${onlyShowNotablesStyleId}' type='text/css'>
  784.           div.reply:not(.post-hover),
  785.           div.post+br {
  786.             display: none !important;
  787.             visibility: hidden !important;
  788.           }
  789.           ${notableOrNominationPostsSelector},
  790.           ${notableOrNominationPostBreaksSelector} {
  791.             display: inline-block !important;
  792.             visibility: visible !important;
  793.           }
  794.           </style>`).appendTo('head');
  795.       } else {
  796.         $(`#${onlyShowNotablesStyleId}`).remove();
  797.         // For whatever reason, when the non notable posts are filtered and new
  798.         // posts come through the auto_update, the posts are created with
  799.         // style="display:block" which messes up display.  Remove style attr
  800.         // TODO: can we remove this now that we have !important?
  801.         $(ResearchBread.POST_SELECTOR).removeAttr('style');
  802.       }
  803.     }
  804.   }
  805.   NotableHighlighter.NOMINATOR_CLASS = 'bakertools-notable-nominator';
  806.   NotableHighlighter.NOTABLE_CLASS = 'bakertools-notable';
  807.  
  808.   /**
  809.    * Wrapper for a post nominated as notable
  810.    */
  811.   class NotablePost {
  812.     /**
  813.      * Construct an empty notable post object
  814.      */
  815.     constructor() {
  816.       this.element = null;
  817.       this.postNumber = null;
  818.       this.description = '[DESCRIPTION]';
  819.       this.nominatingPosts = [];
  820.     }
  821.  
  822.     /**
  823.      * Create a notable post from a nominating post
  824.      *
  825.      * @param {Element} nominatingPost A post that is nominating a notable
  826.      * @return {NotablePost} a Notable post or NullNotablePost if it fails
  827.      */
  828.     static fromNominatingPost(nominatingPost) {
  829.       const [postNumber, element] =
  830.         NotablePost._getNotableFromNominator(nominatingPost);
  831.  
  832.       if (!postNumber) {
  833.         console.info('Could not find a reply link in nominator post');
  834.         console.debug(nominatingPost.querySelector('.body').innerText);
  835.         if (!NotablePost.NULL) {
  836.           NotablePost.NULL = new NullNotablePost();
  837.         }
  838.         return NotablePost.NULL;
  839.       }
  840.  
  841.       let notable = NotablePost.findNotableByPostNumber(postNumber);
  842.       if (!notable) {
  843.         notable = new NotablePost();
  844.  
  845.         const isPreviousBread = element == null;
  846.         if (!isPreviousBread) {
  847.           notable.setElement(element);
  848.         } else {
  849.           // TODO: set pb description
  850.           // get the json from the post number
  851.           notable.postNumber = postNumber;
  852.         }
  853.         NotablePost._notables.push(notable);
  854.       }
  855.       notable.addNominatingPost(nominatingPost);
  856.  
  857.       return notable;
  858.     }
  859.  
  860.     /**
  861.      * Get the post number and the {Element} of a notable post from a
  862.      * nominating post
  863.      *
  864.      * @param {Element} nominatingPost a .div.post that nominates a notable
  865.      * @return {Array} Returns a list with the postNumber and the post element
  866.      */
  867.     static _getNotableFromNominator(nominatingPost) {
  868.       const postNumber = ResearchBread.getFirstReplyLink(nominatingPost);
  869.       const nominatedPost = document.querySelector('#reply_' + postNumber);
  870.       return [postNumber, nominatedPost];
  871.     }
  872.  
  873.     /**
  874.      * Is this a NullNotablePost
  875.      * @return {boolean} false
  876.      */
  877.     isNull() {
  878.       return false;
  879.     }
  880.  
  881.     /**
  882.      * @return {Array<NotablePost>} Array of the current notables
  883.      */
  884.     static getNotables() {
  885.       return NotablePost._notables;
  886.     }
  887.  
  888.     /**
  889.      * @arg {number} postNumber The post number of notable
  890.      * @return {NotablePost}
  891.      */
  892.     static findNotableByPostNumber(postNumber) {
  893.       return NotablePost._notables.find((notable) => notable.postNumber ==
  894.         postNumber);
  895.     }
  896.  
  897.     /**
  898.      * Set the element of the post
  899.      * @arg {Element} element
  900.      */
  901.     setElement(element) {
  902.       this.element = element;
  903.       this._markAsNotable(this.element);
  904.       this.description = element.querySelector('.body')
  905.           .innerText
  906.           .replace(/\n/g, ' ');
  907.       this.postNumber = $(this.element).find('.intro .post_no')
  908.           .text()
  909.           .replace('No.', '');
  910.       console.debug(this.postNumber);
  911.     }
  912.  
  913.     /**
  914.      * Get the reply shortlink for the post
  915.      * @return {string}
  916.      */
  917.     shortLink() {
  918.       return '>>' + this.postNumber;
  919.     }
  920.  
  921.     /**
  922.      * Add a nominator to the notable
  923.      *
  924.      * @param {Element} nominatingPost A .div.post that nominates this post
  925.      */
  926.     addNominatingPost(nominatingPost) {
  927.       this.nominatingPosts.push(nominatingPost);
  928.       this._markAsNominator(nominatingPost);
  929.       this._markNominatorInMentions(nominatingPost);
  930.     }
  931.  
  932.     /**
  933.      * @arg {Element} nominatorPost .post
  934.      */
  935.     _markAsNominator(nominatorPost) {
  936.       console.info(`Mark as nominator: ${nominatorPost}`);
  937.       nominatorPost.classList.add(NotableHighlighter.NOMINATOR_CLASS);
  938.     }
  939.  
  940.     /**
  941.      * @arg {Element} post .post
  942.      */
  943.     _markAsNotable(post) {
  944.       console.info(`Mark as notable: ${post}`);
  945.       post.classList.add(NotableHighlighter.NOTABLE_CLASS);
  946.     }
  947.  
  948.  
  949.     /**
  950.      * Gives links to nominators a special style in notable mentions
  951.      *
  952.      * @param {Element} nominatingPost A .div.post that is nominating this
  953.      *  notable
  954.      */
  955.     _markNominatorInMentions(nominatingPost) {
  956.       const notableString = this.element && this.element.id ||
  957.           this.postNumber + ' (pb)';
  958.       console.info(`Mark as nominator in mentions.  Notable: ${notableString},
  959.         Nominating Post: ${nominatingPost.id}`);
  960.       if (!this.element) {
  961.         console.info(`Notable post is null - possible pb/lb`);
  962.         return;
  963.       }
  964.       const nominatingPostId = nominatingPost.id.replace('reply_', '');
  965.       $(this.element).find('.mentioned-'+nominatingPostId)
  966.           .addClass(NotableHighlighter.NOMINATOR_CLASS);
  967.     }
  968.   }
  969.   NotablePost._notables = [];
  970.   NotablePost.NULL = null; // NullNotablePost
  971.  
  972.   /**
  973.    * A null post that is returned when the post cant be found
  974.    */
  975.   class NullNotablePost extends NotablePost {
  976.     /**
  977.      * Returns true because this is a null post
  978.      * @return {boolean} true
  979.      */
  980.     isNull() {
  981.       return true;
  982.     }
  983.   }
  984.  
  985.   /**
  986.    * Highlights previous bread post links
  987.    */
  988.   class PreviousBreadHighlighter {
  989.     /**
  990.      * Construct pb highlighter object, setup listeners
  991.      */
  992.     constructor() {
  993.       this.previousBreadClass = 'bakertools-PreviousBread';
  994.       this._linkSelector = 'div.body > p.body-line.ltr > a';
  995.  
  996.       this._setupStyles();
  997.  
  998.       const links = $(this._linkSelector).filter('[onClick]');
  999.       links.each(function(index, link) {
  1000.         this.markLinkIfPreviousBread(link);
  1001.       }.bind(this));
  1002.  
  1003.       this._setupListeners();
  1004.     }
  1005.  
  1006.     /**
  1007.      * Setup styles for pb links
  1008.      */
  1009.     _setupStyles() {
  1010.       const sheet = window.document.styleSheets[0];
  1011.       sheet.insertRule(`div.post.reply div.body
  1012.         a.${this.previousBreadClass} {
  1013.           color: #8B0000;
  1014.       }`, sheet.cssRules.length);
  1015.       sheet.insertRule(`a.${this.previousBreadClass}::after {
  1016.         content: " (pb)";
  1017.       }`, sheet.cssRules.length);
  1018.     }
  1019.  
  1020.     /**
  1021.      * Setup listeners for pb highlighting
  1022.      */
  1023.     _setupListeners() {
  1024.       $(document).on('new_post', function(e, post) {
  1025.         $(post).find(this._linkSelector)
  1026.             .each((index, link) => this.markLinkIfPreviousBread(link));
  1027.       }.bind(this));
  1028.     }
  1029.  
  1030.     /**
  1031.      * Marks the link if it is pb
  1032.      *
  1033.      * @param {Anchor} link
  1034.      */
  1035.     markLinkIfPreviousBread(link) {
  1036.       const breadFileName = document.location.pathname.split('/').slice(-1)[0];
  1037.       const linkFileName = link.href.split('/').slice(-1)[0].split('#')[0];
  1038.  
  1039.       if ($(link).attr('onclick').search(ResearchBread.REPLY_REGEX) !=1 &&
  1040.             breadFileName != linkFileName) {
  1041.         $(link).addClass(this.previousBreadClass);
  1042.       }
  1043.     }
  1044.   }
  1045.  
  1046.   /**
  1047.    * Highlight Q posts, replies to q, q replies.
  1048.    * Adds navigation to baker window
  1049.    */
  1050.   class QPostHighlighter {
  1051.     /**
  1052.      * Construct qposthighlighter object and setup listeners
  1053.      */
  1054.     constructor() {
  1055.       this.qPostClass = 'bakertools-q-post';
  1056.       this.qReplyClass = 'bakertools-q-reply';
  1057.       this.qMentionClass = 'bakertools-q-mention';
  1058.       this.qLinkClass = 'bakertools-q-link';
  1059.       this._linkSelector = 'div.body > p.body-line.ltr > a';
  1060.       this.currentQTripCode = null;
  1061.  
  1062.       this._setupStyles();
  1063.       this._getCurrentQTripFromBread();
  1064.       this._findQPosts();
  1065.       this._setupBakerWindowControls();
  1066.       this._setupListeners();
  1067.     }
  1068.  
  1069.     /**
  1070.      * Setup styles for highlighting q posts
  1071.      */
  1072.     _setupStyles() {
  1073.       const sheet = window.document.styleSheets[0];
  1074.       // Dark to light
  1075.       sheet.insertRule(`div.post.reply.${this.qPostClass} {
  1076.         background: #FFFFFF !important;
  1077.         display: inline-block !important;
  1078.         visibility: visible !important;
  1079.       }`, sheet.cssRules.length);
  1080.  
  1081.       // Enlightened by the Q but still not all the way
  1082.       sheet.insertRule(`div.post.reply.${this.qReplyClass} {
  1083.         background: #DDDDDD;
  1084.         display: inline-block !important;
  1085.         visibility: visible !important;
  1086.       }`, sheet.cssRules.length);
  1087.  
  1088.       // Trippy mentions
  1089.       sheet.insertRule(`div.post.reply .intro .${this.qMentionClass},
  1090.       .${this.qLinkClass}
  1091.       {
  1092.         padding:1px 3px 1px 3px;
  1093.         background-color:black;
  1094.         border-radius:8px;
  1095.         border:1px solid #bbbbee;
  1096.         color:gold;
  1097.         background: linear-gradient(300deg, #ff0000, #ff0000, #ff0000, #bbbbbb,
  1098.                   #4444ff);
  1099.         background-size: 800% 800%;
  1100.  
  1101.         -webkit-animation: Patriot 5s ease infinite;
  1102.         -moz-animation: Patriot 5s ease infinite;
  1103.         -o-animation: Patriot 5s ease infinite;
  1104.         animation: Patriot 5s ease infinite;
  1105.         -webkit-text-fill-color: transparent;
  1106.          
  1107.         background: -o-linear-gradient(transparent, transparent);
  1108.         -webkit-background-clip: text;
  1109.       }`, sheet.cssRules.length);
  1110.     }
  1111.  
  1112.     /**
  1113.      * Get Q's current trip code from the bread
  1114.      */
  1115.     _getCurrentQTripFromBread() {
  1116.       const tripCodeMatch = $('div.post.op')
  1117.           .text()
  1118.           .match(/Q's Trip-code: Q (.+?\s)/);
  1119.  
  1120.       if (!tripCodeMatch) {
  1121.         console.error('Could not find Q\'s tripcode');
  1122.         return;
  1123.       }
  1124.       this.currentQTripCode = tripCodeMatch[1].split(' ')[0];
  1125.     }
  1126.  
  1127.     /**
  1128.      * Find current Q posts in bread
  1129.      */
  1130.     _findQPosts() {
  1131.       const posts = ResearchBread.getPostsWithoutDough();
  1132.  
  1133.       $(posts).each(function(i, post) {
  1134.         this._doItQ(post);
  1135.       }.bind(this));
  1136.     }
  1137.  
  1138.     /**
  1139.      * Check if the post is Q
  1140.      * WWG1WGA
  1141.      *
  1142.      * @param {Element} post a div.post
  1143.      */
  1144.     _doItQ(post) {
  1145.       if (this._markIfQPost(post)) { // Q Post, lets check for q replies
  1146.         const qPostNumber = $(post)
  1147.             .find('.intro .post_no')
  1148.             .text()
  1149.             .replace('No.', '');
  1150.  
  1151.         const links = $(post)
  1152.             .find(this._linkSelector)
  1153.             .filter('[onClick]');
  1154.  
  1155.         $(links).each(function(i, link) {
  1156.           const postNumber = link.href.split('#')[1];
  1157.           // Enlightened post
  1158.           $(`#reply_${postNumber}`).addClass(this.qReplyClass);
  1159.  
  1160.           const metionLinkSelector = `#reply_${postNumber} .intro .mentioned a`;
  1161.           $(metionLinkSelector).each(function(i, mentionAnchor) {
  1162.             const mentionPostNumber = $(mentionAnchor).text().replace('>>', '');
  1163.             if (mentionPostNumber == qPostNumber) {
  1164.               $(mentionAnchor).addClass(this.qMentionClass);
  1165.             }
  1166.           }.bind(this));
  1167.         }.bind(this));
  1168.       } else { // Not Q, but lets check if this post replies to Q
  1169.         const links = $(post).find(this._linkSelector).filter('[onClick]');
  1170.  
  1171.         $(links).each(function(i, link) {
  1172.           const postNumber = link.href.split('#')[1];
  1173.           const replyPost = document.querySelector(`#reply_${postNumber}`);
  1174.           if (this._isQ(replyPost)) {
  1175.             $(link).addClass(this.qLinkClass);
  1176.           }
  1177.         }.bind(this));
  1178.       }
  1179.     }
  1180.  
  1181.     /**
  1182.      * @arg {Element} post div.post.reply
  1183.      * @return {boolean} true if it is a q post
  1184.      */
  1185.     _markIfQPost(post) {
  1186.       let isQ = false;
  1187.       if (this._isQ(post)) {
  1188.         $(post).addClass(this.qPostClass);
  1189.         QPostHighlighter.qPosts.push(post);
  1190.         isQ = true;
  1191.         $(document).trigger(QPostHighlighter.NEW_Q_POST_EVENT);
  1192.       }
  1193.       return isQ;
  1194.     }
  1195.  
  1196.     /**
  1197.      * Is the post Q?
  1198.      * @param {Element} post a div.post.reply
  1199.      * @return {boolean} true if the post is Q
  1200.      */
  1201.     _isQ(post) {
  1202.       return this._getTripCodeOfPost(post) == this.currentQTripCode;
  1203.     }
  1204.  
  1205.     /**
  1206.      * Get the trip code of the provided post
  1207.      * @param {Element} post
  1208.      * @return {string} tripcode
  1209.      */
  1210.     _getTripCodeOfPost(post) {
  1211.       return $(post).find('.intro span.trip').text();
  1212.     }
  1213.  
  1214.     /**
  1215.      * Add Q post navigation to bakerwindow
  1216.      */
  1217.     _setupBakerWindowControls() {
  1218.       this.navigation = new NavigationControl('Q Posts',
  1219.           () => QPostHighlighter.qPosts, 'new_q_post');
  1220.       window.bakerTools.mainWindow.addNavigation(this.navigation.element);
  1221.     }
  1222.  
  1223.     /**
  1224.      * Setup listeners for new posts
  1225.      */
  1226.     _setupListeners() {
  1227.       $(document).on('new_post', function(e, post) {
  1228.         this._doItQ(post);
  1229.       }.bind(this));
  1230.     }
  1231.   }
  1232.   QPostHighlighter.qPosts = [];
  1233.   QPostHighlighter.NEW_Q_POST_EVENT = 'bakertools-new-q-post';
  1234.  
  1235.   /**
  1236.    * Add notable button to posts that opens quick reply
  1237.    * and populates with a template message
  1238.    */
  1239.   class NominatePostButtons {
  1240.     /**
  1241.      * Construct NPB object and setup listeners
  1242.      */
  1243.     constructor() {
  1244.       this.bakerNotableHeader = '==BAKER NOTABLE==\n';
  1245.       this.notableReasonPlaceholder = '[REASON FOR NOTABLE HERE]';
  1246.  
  1247.       $('div.post.reply').each(function(i, post) {
  1248.         this._addButtonToPost(post);
  1249.       }.bind(this));
  1250.  
  1251.       $(document).on('new_post', function(e, post) {
  1252.         this._addButtonToPost(post);
  1253.       }.bind(this));
  1254.     }
  1255.  
  1256.     /**
  1257.      * Add button to the provided post
  1258.      * @param {Element} post
  1259.      */
  1260.     _addButtonToPost(post) {
  1261.       const button = document.createElement('button');
  1262.       $(button).append(`
  1263.         <i class="fa fa-star" style="color: goldenrod;"></i>
  1264.         Notable`);
  1265.  
  1266.       $(button).click(function(e) {
  1267.         const postNumber = $(post)
  1268.             .find('.intro .post_no')
  1269.             .text()
  1270.             .replace('No.', '');
  1271.         const href = $(post)
  1272.             .find('.intro .post_no')
  1273.             .get(0).href;
  1274.  
  1275.         // 8kun core - adds >>postnumber to- and unhides quickreply
  1276.         window.citeReply(postNumber, href);
  1277.  
  1278.         const quickReplyBody = $('#quick-reply #body');
  1279.         const oldText = quickReplyBody.val();
  1280.  
  1281.         quickReplyBody.val(oldText + this.bakerNotableHeader +
  1282.           this.notableReasonPlaceholder);
  1283.  
  1284.         // Don't ask me why i have to do this, ask CodeMonkeyZ
  1285.         // Not sure why citeReply which calls cite needs to set a timeout to
  1286.         // replace the body of the quickreply with itself.  We need to combat
  1287.         // that here
  1288.         // setTimeout(function() {
  1289.         //     var tmp = $('#quick-reply textarea[name="body"]').val();
  1290.         //     $('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
  1291.         //  }, 1);
  1292.         // $(window).on('cite', function(e, id, with_link) {
  1293.         // TODO: Figure this out
  1294.         const self = this;
  1295.         setTimeout(function() {
  1296.           quickReplyBody.select();
  1297.           quickReplyBody.prop('selectionStart',
  1298.               oldText.length + self.bakerNotableHeader.length);
  1299.         }, 1.2);
  1300.       }.bind(this));
  1301.  
  1302.       $(post).find('.post_modified').append(button);
  1303.     }
  1304.   }
  1305.  
  1306.   /**
  1307.    * Blur images until highlighted
  1308.    */
  1309.   class BlurImages {
  1310.     /**
  1311.      * Construct blur images object and setup styles
  1312.      */
  1313.     constructor() {
  1314.       this.blurImages = 'bakertools-blur-images';
  1315.       this.blurImagesStyleId = 'bakertools-blur-images-style';
  1316.       window.bakerTools.mainWindow.addOption(`
  1317.         <label for="${this.blurImages}">Blur Images Until Hover</label>
  1318.         <input type="checkbox" id="${this.blurImages}"
  1319.           title="Blur images until mouse hover" /></br>
  1320.       `);
  1321.  
  1322.       $('#'+this.blurImages).change(function(e) {
  1323.         this.setBlurImages(e.target.checked);
  1324.       }.bind(this));
  1325.     }
  1326.  
  1327.     /**
  1328.      * Set whether or not images are blurred
  1329.      * @param {boolean} blurImages if true, blur images
  1330.      */
  1331.     setBlurImages(blurImages) {
  1332.       if (blurImages) {
  1333.         $(`<style id='${this.blurImagesStyleId}' type='text/css'>
  1334.             .post-image {
  1335.                 filter: blur(5px);
  1336.                 transition: all 233ms;
  1337.             }
  1338.             .post-image:hover {
  1339.                 filter: blur(.5px);
  1340.                 transition: all 89ms;
  1341.             }
  1342.         </style>`).appendTo('head');
  1343.       } else {
  1344.         $(`#${this.blurImagesStyleId}`).remove();
  1345.       }
  1346.     }
  1347.   }
  1348.  
  1349.   /**
  1350.    * Highlight posts that (you)
  1351.    * Adds (You) navigation links to baker window
  1352.    */
  1353.   class YouHighlighter {
  1354.     /**
  1355.      * Construct YN object
  1356.      */
  1357.     constructor() {
  1358.       this._setupBakerWindowControls();
  1359.     }
  1360.  
  1361.     /**
  1362.      * Add (you) navigation to bakerwindow
  1363.      */
  1364.     _setupBakerWindowControls() {
  1365.       this.youNavigation = new NavigationControl(`(You)'s`,
  1366.          this._getYous, 'new_post');
  1367.      this.ownNavigation = new NavigationControl(`Own Posts`,
  1368.          this._getOwnPosts, 'new_post');
  1369.  
  1370.      window.bakerTools.mainWindow.addNavigation(this.youNavigation.element);
  1371.      window.bakerTools.mainWindow.addNavigation(this.ownNavigation.element);
  1372.    }
  1373.  
  1374.    /**
  1375.     * Get (You)'s
  1376.      * @return {JQuery}
  1377.      */
  1378.     _getYous() {
  1379.       return $('div.post.reply').filter(function(idx, post) {
  1380.         return post.querySelector('.body')
  1381.             .innerHTML
  1382.             .indexOf('<small>(You)</small>') != -1;
  1383.       });
  1384.     }
  1385.  
  1386.     /**
  1387.      * Get own posts
  1388.      * @return {JQuery}
  1389.      */
  1390.     _getOwnPosts() {
  1391.       return $('div.post.you');
  1392.     }
  1393.   }
  1394.  
  1395.   /**
  1396.   * MAIN
  1397.   */
  1398.   if (ActivePage.isThread()) { // Only setup the tools if we are on a thread
  1399.     $(document).ready(function() {
  1400.       window.bakerTools = {};
  1401.       window.bakerTools.mainWindow = new BakerWindow();
  1402.       new BlurImages();
  1403.       window.bakerTools.notableHighlighter = new NotableHighlighter();
  1404.       window.bakerTools.PreviousBreadHighlighter =
  1405.         new PreviousBreadHighlighter();
  1406.       window.bakerTools.qPostHighlighter = new QPostHighlighter();
  1407.       window.bakerTools.youHighlighter = new YouHighlighter();
  1408.       window.bakerTools.statsOverlay = new StatsOverlay();
  1409.       new NominatePostButtons();
  1410.     });
  1411.   }
  1412. })(window.jQuery);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement