Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- ==8Kun Baker Tools==
- Version 0.4.0
- ==Features:==
- '''Notables'''
- * Highlight posts that are marked notable (I.E. someone has replied and said
- notable) in Yellow
- * Highlight nominating posts in Pink
- * Highlight nominating posts in posts mentions in Green
- * Filter to only nominating and notable posts, Q posts, Q replies
- * Generate notables post
- * NEW IN 0.4.0: Adds "Notable Nomination" button to posts that opens the
- Quick Reply box and prefills it with a BAKER NOTABLE Template
- '''Q Posts'''
- * Highlights Q Posts with white BG -> DARK TO LIGHT!
- * Highlights Q posts in mentions (I.E. posts that get (YOU)'ed)
- * Highlights links to Q Posts
- * Cycle through Q Posts
- '''Comfyness'''
- * Highlight PB links
- * Thread stats overlay with
- * color coded reply count that goes from green to red as bread ages
- * UID Count
- * Jump To Bottom Link
- * Jump To Bottom Top link
- * NEW IN 0.4.0: Option to blur images until hover
- * NEW IN 0.4.0: Cycle through (You)'s
- * NEW IN 0.4.0: Cycle through own posts
- ==To Install:==
- 1. Copy this source code
- 2. Go to 8kun
- 3. Click "Options" in the top right
- 4. Choose "User JS" tab
- 5. Paste Baker tools JS
- 6. WWG1WGA
- ==Changelog:==
- '''0.4.0'''
- * Option to blur images until hover
- * Adds a "Notable Nomination" button to posts that opens the Quick Reply
- box and prefills it with a BAKER NOTABLE Template
- * Add Q Post navigation links to the Baker Window
- * Add (You) navigation links to the Baker Window
- * Add own post navigation links to the Baker Window
- * Cleaned up baker window design
- * More code cleanup and linting changes
- '''0.3.0'''
- * Highlights Q Posts with white BG -> DARK TO LIGHT!
- * Highlights Q posts in mentions (I.E. posts that get (YOU)'ed)
- * Highlights links to Q Posts
- * Refactored code into classes for easier maint.
- '''0.2.0'''
- * Highlight pb links
- * Thread stats overlay with
- * color coded reply count that goes from green to red as bread ages
- * UID Count
- * Jump To Bottom Link
- * Jump To Bottom Top link
- '''0.1.0'''
- Initial release:
- * Highlight notables and nominators
- * Filter to only show notables and nominators
- * Create notables post
- Version History:
- https://pastebin.com/kz9LrcE9 0.3.0
- https://pastebin.com/4aEFsPwK 0.2.0
- https://pastebin.com/eNmTtzdi 0.1.0
- TODO: false positive notables and nominations
- TODO: Bread list
- TODO: Get notable description of pb notables
- TODO: save/Load settings (like for bakerwindow selections)
- TODO: Create Dough?
- TODO: Flood detect
- */
- (function($) {
- 'use strict';
- /**
- * Wrapper for 8kun active_page variable to determine the type of
- * page the user is on.
- */
- class ActivePage {
- /**
- * Are we currently on the thread index page?
- * @return {boolean} True if on index
- */
- static isIndex() {
- return window.active_page == ActivePage.Index;
- }
- /**
- * Are we currently on the thread catalog page?
- * @return {boolean} True if on catalog
- */
- static isCatalog() {
- return window.active_page == ActivePage.Catalog;
- }
- /**
- * Are we on a thread page?
- * @return {boolean} True if on thread
- */
- static isThread() {
- console.log(window.active_page == ActivePage.Thread);
- return window.active_page == ActivePage.Thread;
- }
- }
- ActivePage.Index = 'index';
- ActivePage.Catalog = 'catalog';
- ActivePage.Thread = 'thread';
- /**
- * Creates first, prev, next, last navigation controls
- */
- class NavigationControl {
- /**
- * Construct navigatio control manager object
- *
- * @param {string} label the label for the control
- * @param {Function} updateFunction Called to get latest data
- * @param {string} updateEventName Called to get latest data
- */
- constructor(label, updateFunction, updateEventName) {
- this.navigationControlClass = 'bakertools-navigation-control';
- const strippedName = label.replace(/(\s|\(|\)|'|"|:)/g, '');
- this.label = label;
- this.list = updateFunction();
- $(document).on(updateEventName, () => this.list = updateFunction());
- this.currentIndex = -1;
- this.navigationId = `bakertools-navcontrol-${strippedName}`;
- this.currentIndexId = `${this.navigationId}-current-index`;
- this.totalId = `${this.navigationId}-total`;
- this.goToFirstId = `${this.navigationId}-goto-first`;
- this.goToPreviousId = `${this.navigationId}-goto-prev`;
- this.goToNextId = `${this.navigationId}-goto-next`;
- this.goToLastId = `${this.navigationId}-goto-last`;
- this._createElement(label);
- this._setupListeners();
- }
- /**
- * Create nav element
- */
- _createElement() {
- this.element = $(`
- <span title="Navigate ${this.label}">
- <label for="${this.navigationId}">${this.label}:</label>
- <span id="${this.navigationId}"
- class="${this.navigationControlClass}">
- <i class="fa fa-fast-backward" id="${this.goToFirstId}"></i>
- <i class="fa fa-backward" id="${this.goToPreviousId}"></i>
- <span id="${this.currentIndexId}">
- ${this.currentIndex+1}
- </span>
- of
- <span id="${this.totalId}">${this.list.length}</span>
- <i class="fa fa-forward" id="${this.goToNextId}"></i>
- <i class="fa fa-fast-forward" id="${this.goToLastId}"></i>
- </span>
- </span>
- <br />
- `);
- }
- /**
- * Setup button event listeners
- */
- _setupListeners() {
- this.element.find('#'+this.goToFirstId).click(function(e) {
- this.goToFirstPost();
- }.bind(this));
- this.element.find('#'+this.goToPreviousId).click(function(e) {
- this.goToPreviousPost();
- }.bind(this));
- this.element.find('#'+this.goToNextId).click(function(e) {
- this.goToNextPost();
- }.bind(this));
- this.element.find('#'+this.goToLastId).click(function(e) {
- this.goToLastPost();
- }.bind(this));
- }
- /**
- * Scroll to first post
- */
- goToFirstPost() {
- console.info(`Go to first ${this.label} post`);
- if (!this.list.length) {
- return;
- }
- this.currentIndex = 0;
- this.scrollToCurrentPost();
- }
- /**
- * Scroll to next navigated post
- */
- goToPreviousPost() {
- console.info(`Go to prev ${this.label} post`);
- if (!this.list.length) {
- return;
- }
- if (this.currentIndex == 0) {
- this.currentIndex = this.list.length - 1;
- } else {
- this.currentIndex--;
- }
- this.scrollToCurrentPost();
- }
- /**
- * Scroll to next navigated post
- */
- goToNextPost() {
- console.info(`Go to next ${this.label} post`);
- if (!this.list.length) {
- return;
- }
- const lastPostIndex = this.list.length - 1;
- if (this.currentIndex == lastPostIndex) {
- this.currentIndex = 0;
- } else {
- this.currentIndex++;
- }
- this.scrollToCurrentPost();
- }
- /**
- * Scroll the last post in this bread into view
- */
- goToLastPost() {
- console.info(`Go to last ${this.label} post`);
- if (!this.list.length) {
- return;
- }
- const numPosts = this.list.length;
- this.currentIndex = numPosts - 1;
- this.scrollToCurrentPost();
- }
- /**
- * Scrolls the current selected post into view
- */
- scrollToCurrentPost() {
- const post = this.list[this.currentIndex];
- $(post).get(0).scrollIntoView();
- $('#'+this.currentIndexId).text(this.currentIndex+1);
- window.scrollBy(0, -20);
- }
- }
- /**
- * Research Bread Class
- */
- class ResearchBread {
- /**
- * Get an array of post bodies with dough posts filtered out
- * @return {NodeList} of .post elements
- */
- static getPostsWithoutDough() {
- const posts = Array.from(document
- .querySelectorAll(ResearchBread.POST_SELECTOR));
- const filteredPosts = posts.filter(function(post) {
- return !post.querySelector('.body')
- .innerText.match(ResearchBread.DOUGH_POSTS_REGEX);
- });
- return filteredPosts;
- }
- /**
- * Determine what the bread number is
- * @return {number} the number of the research bread
- */
- static getBreadNumber() {
- const breadNumberRegex = /#(.+?) /;
- return document.querySelector(ResearchBread.OP_SUBJECT_SELECTOR)
- .innerText
- .match(breadNumberRegex)[1] || 'COULD NOT FIND BREAD NUMBER';
- }
- /**
- * @arg {Element} post .post
- * @return {RegexMatch} Capture 1 is the number
- */
- static getFirstReplyLink(post) {
- const match = post.querySelector('.body')
- .innerHTML
- .match(ResearchBread.REPLY_REGEX);
- return match && match[1] || null;
- }
- }
- ResearchBread.OP_SUBJECT_SELECTOR = '.post.op > p > label > span.subject';
- ResearchBread.POST_BODY_SELECTOR = '.post > .body';
- ResearchBread.POST_SELECTOR = '.post';
- ResearchBread.REPLY_REGEX = /highlightReply\('(.+?)'/;
- ResearchBread.DOUGH_POSTS_REGEX = new RegExp(
- `^(Welcome To Q Research General|` +
- `Global Announcements|` +
- `War Room` +
- `|QPosts Archives).*`);
- /**
- * Baker Window
- */
- class BakerWindow {
- /**
- * Construct Baker window element, register listeners
- */
- constructor() {
- this.bakerWindowBodyId = 'bakertools-window-body';
- this.bakerWindowHeaderId = 'bakertools-window-header';
- this.bakerWindowOptionsId = 'bakertools-window-options';
- this.bakerWindowNavigationId = 'bakertools-window-navigation';
- this.bakerWindowBakerId = 'bakertools-window-baker';
- this.bakerWindowCloseId = 'bakertools-window-close';
- this.navigationControlClass = 'bakertools-navigation-control';
- this.id = 'baker-tools-window';
- this.element = null;
- this._createStyles();
- this._createElement();
- this._setupListeners();
- this._setupBakerWindowLink();
- }
- /**
- * Create CSS styles needed by the window
- */
- _createStyles() {
- const sheet = window.document.styleSheets[0];
- // Main window style
- sheet.insertRule(`#${this.id} {
- width: 300px;
- background-color: rgb(214, 218, 240);
- position: fixed;
- z-index: 100;
- float: right;
- right:28.25px;
- border: 1px solid;
- }`, sheet.cssRules.length);
- // Window header headlines
- sheet.insertRule(`#${this.id} #${this.bakerWindowHeaderId} {
- background: #98E;
- border: solid 1px;
- text-align: center;
- margin: 0px;
- }`, sheet.cssRules.length);
- // Window header headlines
- sheet.insertRule(`#${this.id} #${this.bakerWindowHeaderId} h3 {
- margin: 0;
- }`, sheet.cssRules.length);
- // Window close button
- sheet.insertRule(`#${this.id} #${this.bakerWindowCloseId} {
- top: 0px;
- right: 0px;
- position: absolute;
- margin-right: 3px;
- font-size: 20px;
- }`, sheet.cssRules.length);
- // Window body
- sheet.insertRule(`#${this.id} #${this.bakerWindowBodyId} {
- padding: 5px;
- }`, sheet.cssRules.length);
- sheet.insertRule(`#${this.id} details {
- padding: 5px;
- }`, sheet.cssRules.length);
- sheet.insertRule(`#${this.id} summary {
- margin: 0 0 8px;
- font-weight: bold;
- border-bottom: solid 2px;
- }`, sheet.cssRules.length);
- sheet.insertRule(`#${this.id} #${this.bakerWindowNavigationId}
- .${this.navigationControlClass} {
- float: right;
- }`, sheet.cssRules.length);
- }
- /**
- * Create the actual window HTML element
- */
- _createElement() {
- this.element = document.createElement('div');
- this.element.id = this.id;
- this.element.innerHTML = `
- <header id="${this.bakerWindowHeaderId}">
- <h3>Baker Tools</h3>
- <a id="${this.bakerWindowCloseId}" href="javascript:void(0)">
- <i class="fa fa-times"></i>
- </a>
- </header>
- <form id="${this.bakerWindowBodyId}">
- <details id='${this.bakerWindowOptionsId}' open>
- <summary>Options</summary>
- </details>
- <details id='${this.bakerWindowNavigationId}' open>
- <summary>Navigation</summary>
- </details>
- <details id='${this.bakerWindowBakerId}' open>
- <summary>Baker Tools</summary>
- </details>
- </form>
- `;
- document.body.appendChild(this.element);
- $(this.element).draggable();
- $(this.element).hide();
- }
- /**
- * Setup event listeners for bakerwindow
- */
- _setupListeners() {
- $('#'+this.bakerWindowCloseId).click(function(e) {
- this.hide();
- }.bind(this));
- }
- /**
- * Create link for show/hiding baker window, placed in boardlist bar
- */
- _setupBakerWindowLink() {
- this.bakerToolsLink = document.createElement('a');
- this.bakerToolsLink.textContent = '[Baker Tools]';
- this.bakerToolsLink.style.cssText = 'float: right;';
- this.bakerToolsLink.title = 'Baker Tools';
- this.bakerToolsLink.href = 'javascript:void(0)';
- document.querySelector('.boardlist').appendChild(this.bakerToolsLink);
- this.bakerToolsLink.onclick = this.toggle.bind(this);
- }
- /**
- * Show the baker window
- */
- show() {
- $(this.element).css({'top': 15});
- $(this.element).show();
- }
- /**
- * Hide the baker window
- */
- hide() {
- $(this.element).hide();
- }
- /**
- * Is the baker window visible?
- * @return {boolean} true if window is visible
- */
- isVisible() {
- return $(this.element).is(':visible');
- }
- /**
- * Toggle visibility of baker window
- */
- toggle() {
- if (this.isVisible()) {
- this.hide();
- } else {
- this.show();
- }
- }
- /**
- * Add form controls to options section of baker window
- * @arg {Element} htmlContentString form controls
- */
- addOption(htmlContentString) {
- $('#'+this.bakerWindowOptionsId).append(htmlContentString);
- }
- /**
- * Add html elements to the navigation section of the baker window
- * @arg {Element} htmlContentString form controls
- */
- addNavigation(htmlContentString) {
- $('#'+this.bakerWindowNavigationId).append(htmlContentString);
- }
- /**
- * Add html elements to the baker section of the baker window
- * @arg {Element} htmlContentString form controls
- */
- addBaker(htmlContentString) {
- $('#'+this.bakerWindowBakerId).append(htmlContentString);
- }
- } // end class BakerWindow
- /**
- * Overlays bread stats (and some other controls) in the bottom right of the
- * screen.
- * TODO: create add method
- */
- class StatsOverlay {
- /**
- * Construct statsoverlay, html element, setup listeners
- */
- constructor() {
- this.id = 'bakertools-stats-overlay';
- this.maxPosts = 750;
- this.postCountId = 'bakertools-stats-post-count';
- this.userCountId = 'bakertools-stats-uid-count';
- this.qCountId = 'bakertools-stats-q-count';
- this.notableCountId = 'bakertools-stats-notable-count';
- this._createStyles();
- this._createElement();
- this._updateStats();
- $(document).on('new_post', function(e, post) {
- this._updateStats();
- }.bind(this));
- }
- /**
- * Create styles for stats overlay
- */
- _createStyles() {
- const sheet = window.document.styleSheets[0];
- sheet.insertRule(`#${this.id} {
- padding: 5px;
- position: fixed;
- z-index: 100;
- float: right;
- right:28.25px;
- bottom: 28.25px;
- }`, sheet.cssRules.length);
- }
- /**
- * Create actual html element for style overlay
- */
- _createElement() {
- this.element = document.createElement('div');
- this.element.id = this.id;
- this.element.innerHTML = `
- Posts: <span id="${this.postCountId}" ></span>
- UIDS: <span id="${this.userCountId}"></span>
- <a href="#bottom" alt="to-bottom">
- <i class="fa fa-angle-double-down"></i>
- </a>
- <a href="#top" alt="to-top"><i class="fa fa-angle-double-up"></i></a><br/>
- Q's: <span id="${this.qCountId}" ></span>
- Notables: <span id="${this.notableCountId}"></span>
- `;
- document.body.appendChild(this.element);
- }
- /**
- * Update the stats fields
- */
- _updateStats() {
- const postCount = $('#thread_stats_posts').text();
- const progress = postCount/this.maxPosts;
- let postColor = 'green';
- if (progress >= .87) { // ~ 650 posts (100 posts left)
- postColor = 'red';
- } else if (progress >= .5) {
- postColor = 'goldenrod';
- }
- $('#'+this.postCountId).text(postCount).css({'color': postColor});
- $('#'+this.userCountId).text($('#thread_stats_uids').text());
- $('#'+this.qCountId).text(QPostHighlighter.qPosts.length);
- $('#'+this.notableCountId).text(NotablePost.getNotables().length);
- }
- } // End StatsOverlay class
- /**
- * Makes notable posts easier to see by highlighting posts that anons nominate
- * as notable.
- *
- * If someone replies to a post and their post contains the word 'notable',
- * the replied to post will be considered notable.
- *
- * Both the notable post and the nominator posts will be highlighted, as well
- * as the nominator link in the notable's mentions will be highlighted.
- */
- class NotableHighlighter {
- /**
- * Construct notablehighlighter object, find and highlight
- * current notable sand setup listeners
- */
- constructor() {
- this.NOMINATING_REGEX = /notable/i;
- this.showOnlyNotablesCheckboxId = 'bakertools-show-only-notable';
- this.createNotablePostButtonId = 'bakertools-create-notable-post';
- this.notableEditorId = 'bakertools-notable-editor';
- this._createStyles();
- this._setupBakerWindowControls();
- this.findNominatedNotables();
- this._setupListeners();
- }
- /**
- * Create styles that determine how notables are highlighted
- */
- _createStyles() {
- const sheet = window.document.styleSheets[0];
- sheet.insertRule(`div.post.${NotableHighlighter.NOTABLE_CLASS} {
- background-color: #FFFFCC;
- }`, sheet.cssRules.length);
- sheet.insertRule(`div.post.${NotableHighlighter.NOMINATOR_CLASS} {
- background-color: #FFCCE5;
- }`, sheet.cssRules.length);
- sheet.insertRule(`
- div.post.reply .mentioned .${NotableHighlighter.NOMINATOR_CLASS} {
- color: #00CC00;
- font-weight: bold;
- font-size: 1.5em;
- }`, sheet.cssRules.length);
- }
- /**
- * Add controls to the bakerwindow
- */
- _setupBakerWindowControls() {
- const notablePostsTitle = `Only show, notables, nominators, q, q replied
- posts`;
- window.bakerTools.mainWindow.addOption(`
- <label for="${this.showOnlyNotablesCheckboxId}"
- title="${notablePostsTitle}" >
- Only Show Notable/Nomination Posts:
- </label>
- <input type="checkbox" id="${this.showOnlyNotablesCheckboxId}"
- title="${notablePostsTitle}" />
- `);
- window.bakerTools.mainWindow.addBaker(`
- <button type="button" id="${this.createNotablePostButtonId}"
- title="Create notables list post based on current nominated notables" >
- Create Notable Post
- </button>
- <textarea id="${this.notableEditorId}"></textarea>
- `);
- }
- /**
- * Setup listeners for new posts, bakerwindow controls, etc
- */
- _setupListeners() {
- $('#'+this.showOnlyNotablesCheckboxId).change(function(e) {
- this.setOnlyShowNotables(e.target.checked);
- }.bind(this));
- $('#'+this.createNotablePostButtonId).click(function() {
- if ($('#'+this.notableEditorId).val()) {
- if (!confirm(`If you continue, any changes you made will be
- overwritten!`)) {
- return;
- }
- }
- $('#'+this.notableEditorId).val(this.createNotablesPost());
- }.bind(this));
- $(document).on('new_post', function(e, post) {
- this.checkNewPostsForNotables(post);
- }.bind(this));
- }
- /**
- * Create the notables post for review
- * @return {string} Returns the notable post string
- */
- createNotablesPost() {
- const notables = NotablePost.getNotables();
- const breadNumber = ResearchBread.getBreadNumber();
- let post = `'''#${breadNumber}'''\n\n`;
- notables.forEach(function(notable) {
- post += `${notable.shortLink()} ${notable.description}\n\n`;
- });
- return post;
- }
- /**
- * Checks a post for notable nominations
- * @param {Element} post
- */
- checkNewPostsForNotables(post) {
- $(post).removeAttr('style'); // TODO: try removing
- if (this.isNominatingPost(post)) {
- NotablePost.fromNominatingPost(post);
- }
- }
- /**
- * Finds posts that are being tagged as notable.
- *
- * I.E. Finding any post that has been replied to by a post with the string
- * "notable" in it. Maybe at somepoint this can be smarter. Q give me some
- * dwave snow white tech!
- *
- * Highlights notable posts in yellow
- * Highlights nominating posts in pink <3
- * Highlights nominating posts in mentions
- * Add nominee count to post
- * @return {Array<NotablePost>}
- */
- findNominatedNotables() {
- const postsWithoutDough = ResearchBread.getPostsWithoutDough();
- // ^s to ignore notables review posts
- const nominatingPosts = postsWithoutDough
- .filter((post) => this.isNominatingPost(post));
- nominatingPosts.forEach(function(nominatingPost) {
- NotablePost.fromNominatingPost(nominatingPost);
- });
- console.log(NotablePost.getNotables());
- return NotablePost.getNotables();
- }
- /**
- * Is the post nominating a notable
- * @arg {Element} post .post
- * @return {boolean} True if post nominates a notable
- */
- isNominatingPost(post) {
- const postContainsNotable = post.textContent
- .search(this.NOMINATING_REGEX) != -1;
- const postIsReplying = post.querySelector('.body')
- .innerHTML
- .match(ResearchBread.REPLY_REGEX);
- return postContainsNotable && postIsReplying;
- }
- /**
- * Toggle whether only the notable/nominee posts are shown or not
- * @arg {boolean} onlyShowNotables boolean If true, only show
- * notables/nominators, else show all
- */
- setOnlyShowNotables(onlyShowNotables) {
- const notableOrNominationPostsSelector =
- `div.post.${NotableHighlighter.NOTABLE_CLASS},
- div.post.${NotableHighlighter.NOMINATOR_CLASS}`;
- const notableOrNominationPostBreaksSelector =
- `div.post.${NotableHighlighter.NOTABLE_CLASS}+br,
- div.post.${NotableHighlighter.NOMINATOR_CLASS}+br`;
- const onlyShowNotablesStyleId = 'bakertools-only-show-notables';
- if (onlyShowNotables) {
- $(`<style id='${onlyShowNotablesStyleId}' type='text/css'>
- div.reply:not(.post-hover),
- div.post+br {
- display: none !important;
- visibility: hidden !important;
- }
- ${notableOrNominationPostsSelector},
- ${notableOrNominationPostBreaksSelector} {
- display: inline-block !important;
- visibility: visible !important;
- }
- </style>`).appendTo('head');
- } else {
- $(`#${onlyShowNotablesStyleId}`).remove();
- // For whatever reason, when the non notable posts are filtered and new
- // posts come through the auto_update, the posts are created with
- // style="display:block" which messes up display. Remove style attr
- // TODO: can we remove this now that we have !important?
- $(ResearchBread.POST_SELECTOR).removeAttr('style');
- }
- }
- }
- NotableHighlighter.NOMINATOR_CLASS = 'bakertools-notable-nominator';
- NotableHighlighter.NOTABLE_CLASS = 'bakertools-notable';
- /**
- * Wrapper for a post nominated as notable
- */
- class NotablePost {
- /**
- * Construct an empty notable post object
- */
- constructor() {
- this.element = null;
- this.postNumber = null;
- this.description = '[DESCRIPTION]';
- this.nominatingPosts = [];
- }
- /**
- * Create a notable post from a nominating post
- *
- * @param {Element} nominatingPost A post that is nominating a notable
- * @return {NotablePost} a Notable post or NullNotablePost if it fails
- */
- static fromNominatingPost(nominatingPost) {
- const [postNumber, element] =
- NotablePost._getNotableFromNominator(nominatingPost);
- if (!postNumber) {
- console.info('Could not find a reply link in nominator post');
- console.debug(nominatingPost.querySelector('.body').innerText);
- if (!NotablePost.NULL) {
- NotablePost.NULL = new NullNotablePost();
- }
- return NotablePost.NULL;
- }
- let notable = NotablePost.findNotableByPostNumber(postNumber);
- if (!notable) {
- notable = new NotablePost();
- const isPreviousBread = element == null;
- if (!isPreviousBread) {
- notable.setElement(element);
- } else {
- // TODO: set pb description
- // get the json from the post number
- notable.postNumber = postNumber;
- }
- NotablePost._notables.push(notable);
- }
- notable.addNominatingPost(nominatingPost);
- return notable;
- }
- /**
- * Get the post number and the {Element} of a notable post from a
- * nominating post
- *
- * @param {Element} nominatingPost a .div.post that nominates a notable
- * @return {Array} Returns a list with the postNumber and the post element
- */
- static _getNotableFromNominator(nominatingPost) {
- const postNumber = ResearchBread.getFirstReplyLink(nominatingPost);
- const nominatedPost = document.querySelector('#reply_' + postNumber);
- return [postNumber, nominatedPost];
- }
- /**
- * Is this a NullNotablePost
- * @return {boolean} false
- */
- isNull() {
- return false;
- }
- /**
- * @return {Array<NotablePost>} Array of the current notables
- */
- static getNotables() {
- return NotablePost._notables;
- }
- /**
- * @arg {number} postNumber The post number of notable
- * @return {NotablePost}
- */
- static findNotableByPostNumber(postNumber) {
- return NotablePost._notables.find((notable) => notable.postNumber ==
- postNumber);
- }
- /**
- * Set the element of the post
- * @arg {Element} element
- */
- setElement(element) {
- this.element = element;
- this._markAsNotable(this.element);
- this.description = element.querySelector('.body')
- .innerText
- .replace(/\n/g, ' ');
- this.postNumber = $(this.element).find('.intro .post_no')
- .text()
- .replace('No.', '');
- console.debug(this.postNumber);
- }
- /**
- * Get the reply shortlink for the post
- * @return {string}
- */
- shortLink() {
- return '>>' + this.postNumber;
- }
- /**
- * Add a nominator to the notable
- *
- * @param {Element} nominatingPost A .div.post that nominates this post
- */
- addNominatingPost(nominatingPost) {
- this.nominatingPosts.push(nominatingPost);
- this._markAsNominator(nominatingPost);
- this._markNominatorInMentions(nominatingPost);
- }
- /**
- * @arg {Element} nominatorPost .post
- */
- _markAsNominator(nominatorPost) {
- console.info(`Mark as nominator: ${nominatorPost}`);
- nominatorPost.classList.add(NotableHighlighter.NOMINATOR_CLASS);
- }
- /**
- * @arg {Element} post .post
- */
- _markAsNotable(post) {
- console.info(`Mark as notable: ${post}`);
- post.classList.add(NotableHighlighter.NOTABLE_CLASS);
- }
- /**
- * Gives links to nominators a special style in notable mentions
- *
- * @param {Element} nominatingPost A .div.post that is nominating this
- * notable
- */
- _markNominatorInMentions(nominatingPost) {
- const notableString = this.element && this.element.id ||
- this.postNumber + ' (pb)';
- console.info(`Mark as nominator in mentions. Notable: ${notableString},
- Nominating Post: ${nominatingPost.id}`);
- if (!this.element) {
- console.info(`Notable post is null - possible pb/lb`);
- return;
- }
- const nominatingPostId = nominatingPost.id.replace('reply_', '');
- $(this.element).find('.mentioned-'+nominatingPostId)
- .addClass(NotableHighlighter.NOMINATOR_CLASS);
- }
- }
- NotablePost._notables = [];
- NotablePost.NULL = null; // NullNotablePost
- /**
- * A null post that is returned when the post cant be found
- */
- class NullNotablePost extends NotablePost {
- /**
- * Returns true because this is a null post
- * @return {boolean} true
- */
- isNull() {
- return true;
- }
- }
- /**
- * Highlights previous bread post links
- */
- class PreviousBreadHighlighter {
- /**
- * Construct pb highlighter object, setup listeners
- */
- constructor() {
- this.previousBreadClass = 'bakertools-PreviousBread';
- this._linkSelector = 'div.body > p.body-line.ltr > a';
- this._setupStyles();
- const links = $(this._linkSelector).filter('[onClick]');
- links.each(function(index, link) {
- this.markLinkIfPreviousBread(link);
- }.bind(this));
- this._setupListeners();
- }
- /**
- * Setup styles for pb links
- */
- _setupStyles() {
- const sheet = window.document.styleSheets[0];
- sheet.insertRule(`div.post.reply div.body
- a.${this.previousBreadClass} {
- color: #8B0000;
- }`, sheet.cssRules.length);
- sheet.insertRule(`a.${this.previousBreadClass}::after {
- content: " (pb)";
- }`, sheet.cssRules.length);
- }
- /**
- * Setup listeners for pb highlighting
- */
- _setupListeners() {
- $(document).on('new_post', function(e, post) {
- $(post).find(this._linkSelector)
- .each((index, link) => this.markLinkIfPreviousBread(link));
- }.bind(this));
- }
- /**
- * Marks the link if it is pb
- *
- * @param {Anchor} link
- */
- markLinkIfPreviousBread(link) {
- const breadFileName = document.location.pathname.split('/').slice(-1)[0];
- const linkFileName = link.href.split('/').slice(-1)[0].split('#')[0];
- if ($(link).attr('onclick').search(ResearchBread.REPLY_REGEX) !=1 &&
- breadFileName != linkFileName) {
- $(link).addClass(this.previousBreadClass);
- }
- }
- }
- /**
- * Highlight Q posts, replies to q, q replies.
- * Adds navigation to baker window
- */
- class QPostHighlighter {
- /**
- * Construct qposthighlighter object and setup listeners
- */
- constructor() {
- this.qPostClass = 'bakertools-q-post';
- this.qReplyClass = 'bakertools-q-reply';
- this.qMentionClass = 'bakertools-q-mention';
- this.qLinkClass = 'bakertools-q-link';
- this._linkSelector = 'div.body > p.body-line.ltr > a';
- this.currentQTripCode = null;
- this._setupStyles();
- this._getCurrentQTripFromBread();
- this._findQPosts();
- this._setupBakerWindowControls();
- this._setupListeners();
- }
- /**
- * Setup styles for highlighting q posts
- */
- _setupStyles() {
- const sheet = window.document.styleSheets[0];
- // Dark to light
- sheet.insertRule(`div.post.reply.${this.qPostClass} {
- background: #FFFFFF !important;
- display: inline-block !important;
- visibility: visible !important;
- }`, sheet.cssRules.length);
- // Enlightened by the Q but still not all the way
- sheet.insertRule(`div.post.reply.${this.qReplyClass} {
- background: #DDDDDD;
- display: inline-block !important;
- visibility: visible !important;
- }`, sheet.cssRules.length);
- // Trippy mentions
- sheet.insertRule(`div.post.reply .intro .${this.qMentionClass},
- .${this.qLinkClass}
- {
- padding:1px 3px 1px 3px;
- background-color:black;
- border-radius:8px;
- border:1px solid #bbbbee;
- color:gold;
- background: linear-gradient(300deg, #ff0000, #ff0000, #ff0000, #bbbbbb,
- #4444ff);
- background-size: 800% 800%;
- -webkit-animation: Patriot 5s ease infinite;
- -moz-animation: Patriot 5s ease infinite;
- -o-animation: Patriot 5s ease infinite;
- animation: Patriot 5s ease infinite;
- -webkit-text-fill-color: transparent;
- background: -o-linear-gradient(transparent, transparent);
- -webkit-background-clip: text;
- }`, sheet.cssRules.length);
- }
- /**
- * Get Q's current trip code from the bread
- */
- _getCurrentQTripFromBread() {
- const tripCodeMatch = $('div.post.op')
- .text()
- .match(/Q's Trip-code: Q (.+?\s)/);
- if (!tripCodeMatch) {
- console.error('Could not find Q\'s tripcode');
- return;
- }
- this.currentQTripCode = tripCodeMatch[1].split(' ')[0];
- }
- /**
- * Find current Q posts in bread
- */
- _findQPosts() {
- const posts = ResearchBread.getPostsWithoutDough();
- $(posts).each(function(i, post) {
- this._doItQ(post);
- }.bind(this));
- }
- /**
- * Check if the post is Q
- * WWG1WGA
- *
- * @param {Element} post a div.post
- */
- _doItQ(post) {
- if (this._markIfQPost(post)) { // Q Post, lets check for q replies
- const qPostNumber = $(post)
- .find('.intro .post_no')
- .text()
- .replace('No.', '');
- const links = $(post)
- .find(this._linkSelector)
- .filter('[onClick]');
- $(links).each(function(i, link) {
- const postNumber = link.href.split('#')[1];
- // Enlightened post
- $(`#reply_${postNumber}`).addClass(this.qReplyClass);
- const metionLinkSelector = `#reply_${postNumber} .intro .mentioned a`;
- $(metionLinkSelector).each(function(i, mentionAnchor) {
- const mentionPostNumber = $(mentionAnchor).text().replace('>>', '');
- if (mentionPostNumber == qPostNumber) {
- $(mentionAnchor).addClass(this.qMentionClass);
- }
- }.bind(this));
- }.bind(this));
- } else { // Not Q, but lets check if this post replies to Q
- const links = $(post).find(this._linkSelector).filter('[onClick]');
- $(links).each(function(i, link) {
- const postNumber = link.href.split('#')[1];
- const replyPost = document.querySelector(`#reply_${postNumber}`);
- if (this._isQ(replyPost)) {
- $(link).addClass(this.qLinkClass);
- }
- }.bind(this));
- }
- }
- /**
- * @arg {Element} post div.post.reply
- * @return {boolean} true if it is a q post
- */
- _markIfQPost(post) {
- let isQ = false;
- if (this._isQ(post)) {
- $(post).addClass(this.qPostClass);
- QPostHighlighter.qPosts.push(post);
- isQ = true;
- $(document).trigger(QPostHighlighter.NEW_Q_POST_EVENT);
- }
- return isQ;
- }
- /**
- * Is the post Q?
- * @param {Element} post a div.post.reply
- * @return {boolean} true if the post is Q
- */
- _isQ(post) {
- return this._getTripCodeOfPost(post) == this.currentQTripCode;
- }
- /**
- * Get the trip code of the provided post
- * @param {Element} post
- * @return {string} tripcode
- */
- _getTripCodeOfPost(post) {
- return $(post).find('.intro span.trip').text();
- }
- /**
- * Add Q post navigation to bakerwindow
- */
- _setupBakerWindowControls() {
- this.navigation = new NavigationControl('Q Posts',
- () => QPostHighlighter.qPosts, 'new_q_post');
- window.bakerTools.mainWindow.addNavigation(this.navigation.element);
- }
- /**
- * Setup listeners for new posts
- */
- _setupListeners() {
- $(document).on('new_post', function(e, post) {
- this._doItQ(post);
- }.bind(this));
- }
- }
- QPostHighlighter.qPosts = [];
- QPostHighlighter.NEW_Q_POST_EVENT = 'bakertools-new-q-post';
- /**
- * Add notable button to posts that opens quick reply
- * and populates with a template message
- */
- class NominatePostButtons {
- /**
- * Construct NPB object and setup listeners
- */
- constructor() {
- this.bakerNotableHeader = '==BAKER NOTABLE==\n';
- this.notableReasonPlaceholder = '[REASON FOR NOTABLE HERE]';
- $('div.post.reply').each(function(i, post) {
- this._addButtonToPost(post);
- }.bind(this));
- $(document).on('new_post', function(e, post) {
- this._addButtonToPost(post);
- }.bind(this));
- }
- /**
- * Add button to the provided post
- * @param {Element} post
- */
- _addButtonToPost(post) {
- const button = document.createElement('button');
- $(button).append(`
- <i class="fa fa-star" style="color: goldenrod;"></i>
- Notable`);
- $(button).click(function(e) {
- const postNumber = $(post)
- .find('.intro .post_no')
- .text()
- .replace('No.', '');
- const href = $(post)
- .find('.intro .post_no')
- .get(0).href;
- // 8kun core - adds >>postnumber to- and unhides quickreply
- window.citeReply(postNumber, href);
- const quickReplyBody = $('#quick-reply #body');
- const oldText = quickReplyBody.val();
- quickReplyBody.val(oldText + this.bakerNotableHeader +
- this.notableReasonPlaceholder);
- // Don't ask me why i have to do this, ask CodeMonkeyZ
- // Not sure why citeReply which calls cite needs to set a timeout to
- // replace the body of the quickreply with itself. We need to combat
- // that here
- // setTimeout(function() {
- // var tmp = $('#quick-reply textarea[name="body"]').val();
- // $('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
- // }, 1);
- // $(window).on('cite', function(e, id, with_link) {
- // TODO: Figure this out
- const self = this;
- setTimeout(function() {
- quickReplyBody.select();
- quickReplyBody.prop('selectionStart',
- oldText.length + self.bakerNotableHeader.length);
- }, 1.2);
- }.bind(this));
- $(post).find('.post_modified').append(button);
- }
- }
- /**
- * Blur images until highlighted
- */
- class BlurImages {
- /**
- * Construct blur images object and setup styles
- */
- constructor() {
- this.blurImages = 'bakertools-blur-images';
- this.blurImagesStyleId = 'bakertools-blur-images-style';
- window.bakerTools.mainWindow.addOption(`
- <label for="${this.blurImages}">Blur Images Until Hover</label>
- <input type="checkbox" id="${this.blurImages}"
- title="Blur images until mouse hover" /></br>
- `);
- $('#'+this.blurImages).change(function(e) {
- this.setBlurImages(e.target.checked);
- }.bind(this));
- }
- /**
- * Set whether or not images are blurred
- * @param {boolean} blurImages if true, blur images
- */
- setBlurImages(blurImages) {
- if (blurImages) {
- $(`<style id='${this.blurImagesStyleId}' type='text/css'>
- .post-image {
- filter: blur(5px);
- transition: all 233ms;
- }
- .post-image:hover {
- filter: blur(.5px);
- transition: all 89ms;
- }
- </style>`).appendTo('head');
- } else {
- $(`#${this.blurImagesStyleId}`).remove();
- }
- }
- }
- /**
- * Highlight posts that (you)
- * Adds (You) navigation links to baker window
- */
- class YouHighlighter {
- /**
- * Construct YN object
- */
- constructor() {
- this._setupBakerWindowControls();
- }
- /**
- * Add (you) navigation to bakerwindow
- */
- _setupBakerWindowControls() {
- this.youNavigation = new NavigationControl(`(You)'s`,
- this._getYous, 'new_post');
- this.ownNavigation = new NavigationControl(`Own Posts`,
- this._getOwnPosts, 'new_post');
- window.bakerTools.mainWindow.addNavigation(this.youNavigation.element);
- window.bakerTools.mainWindow.addNavigation(this.ownNavigation.element);
- }
- /**
- * Get (You)'s
- * @return {JQuery}
- */
- _getYous() {
- return $('div.post.reply').filter(function(idx, post) {
- return post.querySelector('.body')
- .innerHTML
- .indexOf('<small>(You)</small>') != -1;
- });
- }
- /**
- * Get own posts
- * @return {JQuery}
- */
- _getOwnPosts() {
- return $('div.post.you');
- }
- }
- /**
- * MAIN
- */
- if (ActivePage.isThread()) { // Only setup the tools if we are on a thread
- $(document).ready(function() {
- window.bakerTools = {};
- window.bakerTools.mainWindow = new BakerWindow();
- new BlurImages();
- window.bakerTools.notableHighlighter = new NotableHighlighter();
- window.bakerTools.PreviousBreadHighlighter =
- new PreviousBreadHighlighter();
- window.bakerTools.qPostHighlighter = new QPostHighlighter();
- window.bakerTools.youHighlighter = new YouHighlighter();
- window.bakerTools.statsOverlay = new StatsOverlay();
- new NominatePostButtons();
- });
- }
- })(window.jQuery);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement