11
\$\begingroup\$

So Stackoverflow Documentation has added "Contributor Breakdown". For any given topic (and example) people can display who contributed in which way.

That page is only available if you know the link though. And it's not linked publicly in any way. Since I like these hidden pages, but I keep forgetting the correct URLs to use, I wrote a short userscript to insert these links where relevant:

// ==UserScript==
// @name         Documentation contributor links
// @namespace    http://github.com/Vogel612/mini-se-userscripts/documentation-contributor-breakdown
// @version      0.1
// @description  Add links to the contributor breakdown for topics and examples on stackoverflow documentation.
// @author       Vogel612
// @include      /https?:\/\/stackoverflow\.com\/documentation\/.*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    // these are in here for debugging purposes
    document.addLinks = addLinks;
    document.addContributorLink = addContributorLink;
})();
// because the page is modified on SE side after loading. 2 secs should be enough to wait
setTimeout(addLinks, 2000);

function addLinks() {
    'use strict';
    let topicLink = document.querySelectorAll("a.doc-topic-link")[0];
    let topicMenuContainer = document.querySelector("div.docs-menu.topic-menu");
    addContributorLink("topic", topicLink.href.split('/')[5], topicMenuContainer);

    let exampleLinks = document.querySelectorAll(".example-link a.doc-example-link");
    let exampleMenus = document.querySelectorAll(".example-link div.example-menu.docs-menu");
    for (var i = 0; i < exampleLinks.length; i++) {
        let currentLink = exampleLinks[i];
        let currentMenuContainer = exampleMenus[i];
        addContributorLink("example", currentLink.href.split('/')[7], currentMenuContainer);
    }
}

function addContributorLink(section, linkId, menu) {
    'use strict';
    let href = `/documentation/contributors/${section}/${linkId}`;
    let elem = document.createElement("A");
    elem.href = href;
    elem.innerHTML = "CB";
    elem.title = "Contributor Breakdown";
    menu.insertBefore(elem, menu.children[0]);
}

Note that the documentation pages are modified by StackOverflow after loading and that removes the link, if I add it before the page is completely built. That's why I run addLinks() with a 2 second delay.

Any and all aspects are up for critique, as always :)

\$\endgroup\$
  • 1
    \$\begingroup\$ document.querySelectorAll("a.doc-topic-link")[0] ==> document.querySelector("a.doc-topic-link") \$\endgroup\$ – Tushar Sep 13 '16 at 15:54
4
\$\begingroup\$
  1. No need for timeout, just attach in a place that doesn't get overwritten: after the title.
  2. Since the extracted link id number is always the last, we can process all links in one go, extracting section name from a link's class name via doc-(\w+)-link to make the pattern self-evident.
  3. Instead of magic split indexes I suggest using a simple \d regexp to extract all digits.
  4. Building DOM elements by individual properties is unreadable, it's a thing from '00s, I suggest using the equally fast (or faster) and much more readable insertAdjacentHTML.

The entire code boils down to:

for (let link of document.querySelectorAll("a.doc-topic-link, a.doc-example-link")) {
    let section = link.className.match(/doc-(\w+)-link/).pop();
    let lastNumber = new URL(link.href).pathname.match(/(\d+)/g).pop();
    link.insertAdjacentHTML('afterend', `
        <a href="/documentation/contributors/${section}/${lastNumber}"
           title="Contributor Breakdown">CB</a>
    `);
}
\$\endgroup\$
  • \$\begingroup\$ re your third point ... thinking about it a little more it's unfortunately not quite as simple as you make it to be, because the link to examples still contains the id of the containing topic. Accordingly what you recommend doesn't work quite as easily there :) Adding it after the title is a somewhat interesting idea, but I think it's semantically incorrect to do so, so I won't do it ... \$\endgroup\$ – Vogel612 Oct 3 '16 at 11:27
  • \$\begingroup\$ #3 is okay because it gets the last number, although I can make it more reliable: let lastNumber = new URL(link.href).pathname.match(/\/(\d+)\//g).pop().slice(1, -1); As for #1, it's now invalid since the updated docs site overrides the click event, but semantic considerations are much less important when hacking something. \$\endgroup\$ – wOxxOm Oct 3 '16 at 11:41
  • \$\begingroup\$ For example, #1 can be implemented by inserting the link into a topic/example container: p.ip.fi/Tu8L \$\endgroup\$ – wOxxOm Oct 3 '16 at 12:00
8
\$\begingroup\$

You're using 'use strict'; in all functions except the top one. Why not declare it at the top of your file?

You seem to have thought about readability. You're declaring variables for just that purpose here:

    let currentLink = exampleLinks[i];
    let currentMenuContainer = exampleMenus[i];

So why oh why do I see magic numbers in your code?

setTimeout(addLinks, 2000);
let topicLink = document.querySelectorAll("a.doc-topic-link")[0];
topicLink.href.split('/')[5]
currentLink.href.split('/')[7]
menu.insertBefore(elem, menu.children[0]);

Of those 5, arguably the first and latter are acceptable. The 3rd and 4th are the most magical of the lot. You explained the first in a comment, but I'd personally move it to a variable.

\$\endgroup\$
  • \$\begingroup\$ Because if you put it at the top of the file outside a function you get use the function form of "use strict"; which is annoying with linters \$\endgroup\$ – cat Sep 13 '16 at 18:07
  • 1
    \$\begingroup\$ there's also a technical reason. unless you wrap the userscript in an IIFE, putting 'use strict' at the top of the file will cause all following scripts to execute in strict mode - and some of them may not have been designed to! Granted in 2016 this shouldn't be an issue, but you never know. Vogel should still wrap his code in an IIFE and use the 'use strict' prologue there, which is what the linter warning refers to \$\endgroup\$ – Dan Pantry Sep 13 '16 at 19:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.