The Return of the view raw button


  • FoxDev

    @FrostCat said:

    @RaceProUK Also, we really need the equivalent of the View Raw button back.

    my DOM manioulation skills fail me, this will add a button, fetch the raw and display it in an annoying alert.

    one cookie to anyone who wants to make it do the whole toggle between showing the raw and the cooked on subsequent clicks, and also for making it look better.

    // ==UserScript==
    // @name         View Raw Button
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    'use strict';
    
    $(window).on('action:ajaxify.contentLoaded', function(){
        $('.post-tools:not(a.viewRaw)').prepend('<a class="viewRaw" href="#" class="no-select">View Raw</a>');
    });
    $('.post-tools:not(a.viewRaw)').prepend('<a class="viewRaw" href="#" class="no-select">View Raw</a>');
    $(document).on('click', 'a.viewRaw', null, function(){
        var post = $(this).closest('li[component=post]');
        var pid = post.data('pid');
        socket.emit('posts.getRawPost', pid, function(err,rawContent){
            if (err){
                alert(err);
            }
            alert(rawContent);
        });
    });
    // Your code here...
    

  • Discourse touched me in a no-no place

    @accalia said:

    one cookie to anyone who wants to make it do the whole toggle

    Sigh. But then I'd have to learn more JS.


  • FoxDev

    @FrostCat fiiiiiine......

    if it's frostcat he can have the whole dozen cookies.

    i was going to share them with the orphans.... but if you need them more....


  • Notification Spam Recipient

    You're like, one line between appending the content to the dom and toggling it back, methinks....


  • FoxDev

    @Tsaukpaetra have at it my friend then.

    i fiddels with it for ten minutes or so before deciding to give it to y'all to solve instead.


  • Notification Spam Recipient

    @accalia said:

    have at it my friend then.

    Awe darn, challenge received just before I go to excercises... Hmm choices...


  • Discourse touched me in a no-no place

    If it helps, here's @Onyx47's code for the Discourse button.



  • I got bored and poked at this a bit.

    There's probably a prettier way to handle the raw formatting than just wrapping it in <pre> tags and calling it good, or duplicating the other buttons styles to format the link, but I'm not feeling like working it out right now.

    Undefined behavior will result if the post is updated while you're viewing raw. There's probably an event (on that magic socket black box thing I'm being too lazy to Google?) I could listen to for updates, but I'm not feeling it right now.

    // ==UserScript==
    // @name         View Raw Button
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    'use strict';
    
    var viewRawButton = '<a class="view-raw no-select" href="#">View Raw</a>';
    
    $('<style>a.view-raw { background-color: #337ab7; color: white; padding: 10px; margin-right: 3px; }</style>').appendTo('head');
    
    $(window).on('action:ajaxify.contentLoaded', function(){
        $('.post-tools:not(a.view-raw)').prepend(viewRawButton);
        $('a.view-raw').off('click', showRaw);
        $('a.view-raw').on('click', showRaw);
    });
    $('.post-tools:not(a.view-raw)').prepend(viewRawButton);
    $('a.view-raw').off('click', showRaw);
    $('a.view-raw').on('click', showRaw);
    
    function showRaw() {
        var button = $(this);
        var post = button.closest('li[component=post]');
        var pid = post.data('pid');
        socket.emit('posts.getRawPost', pid, function(err, rawContent) {
            if (err) {
                if (console && console.error) {
                    console.error(err);
                }
                else {
                    alert(err);
                }
            }
            var contentElement = post.find('div.content');
            var oldContentHtml = contentElement.html();
            var oldHeight = contentElement.height();
            
            var encodedRaw = $('<div/>').text(rawContent).html();
            
            // String concatination is the best way to build any DOM element
            contentElement.html('<pre>' + encodedRaw + '</pre>');
            // prevent some ugly height popping
            if (contentElement.height() < oldHeight) {
                contentElement.height(oldHeight);
            }
            
            button.off('click', showRaw);
            button.on('click', showContent);
            
            function showContent() {
                contentElement.html(oldContentHtml);
                
                button.off('click', showContent);
                button.on('click', showRaw);
            }
        });
    }
    

    Edited to fix a couple bugs.


  • FoxDev

    @AngleOSaxon socket is just the socket.io websocket that the forum uses to communicate with the server. i hijacked it for getting the post raw. ;-)



  • @AngleOSaxon said:

    just wrapping it in <pre> tags

    Long lines of text don't wrap. Other than that, it works really well.


  • BINNED

    @NedFodder Bootstrap has decently styled <code> tags IIRC, but I don't know how they handle wrapping in that.



  • @Onyx I just tried <code> and I have somewhat the opposite problem. Text wraps just fine, but all newlines are stripped out.


  • BINNED

    @NedFodder In that case, I guess a <pre> with a custom class will be the easiest?



  • @NedFodder said:

    newlines are stripped out

    :wtf:



  • @Onyx That's beyond my skills.

    Also, when new posts stream in, they don't get the view raw button.


  • BINNED

    @hungrier HTML ignores \ns and \rs. That's why there's a <br> tag. <pre> element is special in that regard: it will render the plaintext representation of what ever you give it.

    @NedFodder: try http://stackoverflow.com/a/248013



  • @Onyx Yes, that works beautifully. So I can apply the style to all pre thingys, what I don't know how to do is that custom class stuffs. Do you mean something like <pre class="raw-post-content">?


  • BINNED

    @NedFodder That's what I was thinking, yes. Maybe also add a border or something, but that's 🚲 🏡 territory...

    And you change the CSS from pre { ... to pre.raw-post-content { ..., of course ;)


  • FoxDev

    @NedFodder said:

    Also, when new posts stream in, they don't get the view raw button.

    must be missing an event then.....



  • @Onyx I thought <code> might be special in a similar way. However I did find while fiddling that white-space: pre-wrap; looks pretty good for line-wrapped, preformatted code.



  • @accalia said:

    must be missing an event then.....

    I changed it from ajaxify.contentLoaded to posts.loading and it seems to work.

    Next problem, @Onyx : The pre stuff is wrapping the text, but it does it in the middle of words instead of on whitespace.



  • @NedFodder said:

    it seems to work.

    Actually, no it doesn't. Back to the drawing board...


  • BINNED

    @NedFodder said:

    Next problem, @Onyx : The pre stuff is wrapping the text, but it does it in the middle of words instead of on whitespace.

    Darn, didn't expect that... Maybe fiddle with word-wrap CSS property? I honestly don't know off the top of my head, and still at work now so no time to play with it much.



  • @NedFodder

    I think pre-line instead of pre-wrap does what you want.



  • @AngleOSaxon That didn't change anything for me, but word-break: normal; worked.



  • @FrostCat Just put the container for the raw in the same LI in the same position as the content class, and swap visibilities. You can load the raw on demand.



  • Actually, posts.loaded did work. A little too well. It prepends the button every time a new post (or batch of posts) streams in. I got up to 4 buttons at one point...



  • @NedFodder OK, I solved that problem by removing all the buttons and adding them back on posts.loaded (with a combination of getElementsByClassName("view-raw") and removeChild). There's gotta be a better way...


  • FoxDev

    @NedFodder Check if they exist before adding them?



  • @RaceProUK I couldn't figure out how to do that. On a related note, I don't know anything about JS, DOM, CSS, etc...


  • I survived the hour long Uno hand

    Did you try <pre><code>? That's pretty standard for multi-line code blocks.


  • BINNED

    Okay, Imma go dive into the docs, see if I can suss out the plugin architecture and make a proper Thing™.


  • BINNED

    Ok, so, update, NodeBB's docs on building plugins are... not great but it all looks reasonable enough, shouldn't be much of a problem.

    However!

    It seems that plugins have to be npm packages... Meaning I now have to learn npm in order to even start hacking away. Yay.

    ... can we go back to userscripts?


  • I survived the hour long Uno hand

    @Onyx If you can get the JS working, the other sockdevs have published stuff to NPM before and can probably show you how


  • BINNED

    @Yamikuronue The problem is that it seems that it has to be a node module for it to even get loaded by NodeBB. The docs would seem to suggest there's another way but it's not described anywhere.


  • ♿ (Parody)

    @Yamikuronue said:

    If you can get the JS working, the other sockdevs have published stuff to NPM before and can probably show you how

    Or @ben_lubar will have it deployed while you are still shitting around with your pants around your ankles.



  • The $('a.view-raw').on('click', showRaw); stuff should all be removed and replaced with $(document).on('click', '.view-raw', showRaw); (just once, don't need it in the event handler). (Edit: dammit, that worked for a while and now it doesn't work after a refresh. Greasemonkey is pissing me off!)

    @Onyx @boomzilla Here's the plugin code to add a button to every post:



  • @Onyx said:

    The problem is that it seems that it has to be a node module for it to even get loaded by NodeBB. The docs would seem to suggest there's another way but it's not described anywhere.

    As near as I can tell, being a node module isn't required, but looking like one is. Seems like you can imitate the expected format well enough and drop a folder into node_modules to have NodeBB pick it up without publishing it (see below).

    I uploaded a bare-bones plugin that adds the View Raw button to Github here, if you want to take that as a starting point. It's not published as an npm package (not even sure the packages.json has enough data to be valid for npm), but you ought to be able to git clone it down into your node_modules folder and restart your instance of NodeBB to have it picked up.

    Seemed to work okay while I was playing with it on my test instance, but you know what they say about programmers testing their own code.


    I haven't checked the code, but NodeBB appears to search the nodebb/node_modules/ directory for any folders prefixed with nodebb-plugin, and checks those to see if they have the right definition files (package.json and plugin.json) to be plugins.

    It's important to note that, despite what the docs imply about NodeBB only using plugin.json, both files are required, and metadata is pulled from both.

    The package name, description, and version number will all be pulled from the package.json file (which needs to look like an npm package file), while the support URL and nodebb-relevant configuration will be pulled from plugin.json.

    If package.json doesn't exist, NodeBB will not deign to notice your plugin. If name or version number aren't set, it will crash.



  • @NedFodder said:

    Actually, posts.loaded did work. A little too well. It prepends the button every time a new post (or batch of posts) streams in. I got up to 4 buttons at one point...

    Try using '.post-tools a:first-child[component="post/reply"]' to select Reply buttons if they're the first child. Once you add the raw button, the Reply button isn't the first child so you won't get more than one raw button.

    edit: okay, this really isn't a good method because it assumes that things inside .post-tools won't ever change. Better would be to use @accalia's modified jQuery code, $('.post-tools:not(:has(a.viewRaw))'), or filter it with Javascript as I did in the code I posted further down this topic.


  • FoxDev

    @anotherusername personally i go for $('.post-tools:not(a.viewRaw)') as that selects the post tools sections that don't have a view raw button (assuming a.viewRaw matches the view raw button) so that it's no longer order dependent


  • I survived the hour long Uno hand

    @AngleOSaxon said:

    As near as I can tell, being a node module isn't required, but looking like one is.

    In Node, the map is the territory: if you export anything to module.exports, you're a module. NPM publication is never really required, it just has to be someplace NPM can get at it if you want it to be able to be installed. NPM can install from Github, from a git URL, from a number of places.


  • BINNED

    Status update: Ok, figured out how to package the raw with the post so we don't have to pull it on each click. Hopefully it's not much of an overhead. Unfortunately, the button adding thing @NedFodder linked to doesn't really work. I'll try poking around the templates, see if something got renamed.


  • FoxDev

    @Yamikuronue said:

    if you export anything to module.exportsare a .js file, you're a module.

    @Yamikuronue said:

    NPM publication is never really required, it just has to be someplace NPM can get at it if you want it to be able to be installed.

    you also need a package.json to be installed via NPM

    yes, that is overloading the term a bit to call individual files modules as well as what you install via NPM....

    basically a module is anything that you can require() and NPM installs packages that happen to be treated as modules by require()

    i wish they had come up with better terminology.


  • I survived the hour long Uno hand

    @accalia said:

    basically a module is anything that you can require()

    I beg to differ, unless you consider a flat JSON file a "module" 😸



  • @accalia said:

    @anotherusername personally i go for $('.post-tools:not(a.viewRaw)') as that selects the post tools sections that don't have a view raw button (assuming a.viewRaw matches the view raw button) so that it's no longer order dependent

    Did you try it? $('.post-tools:not(a.viewRaw)') is still selecting .post-tools with a.viewRaw child elements...

    0_1458923968451_Untitled.png

    That seems correct, anyway. The .not pseudo-class applies to the node you're trying to select. You're selecting the .post-tools node, which doesn't match a.viewRaw, so it matches .post-tools:not(a.viewRaw). It doesn't matter if it contains a node that matches a.viewRaw; that's not the node being selected so it doesn't affect the rule at all

    To actually make it order-independent, you wouldn't be able to do it in pure CSS... you'd need to select .post-tools and then use Javascript to filter out any that contain a.viewRaw descendant nodes.


  • BINNED

    Anyone have the source to that userscript? post-tools is the stuff in the dotburger menu, no?


  • FoxDev

    @anotherusername said:

    $('.post-tools:not(a.viewRaw)')

    huh... okay then.....

    $('.post-tools:not(:has(a.viewRaw))')

    that works.



  • @Onyx said:

    post-tools is the stuff in the dotburger menu, no?

    No.

    0_1458924783449_Untitled.png


  • FoxDev

    @Onyx said:

    post-tools is the stuff in the dotburger menu, no?

    The dotburger is moderator-tools



  • @Yamikuronue said:

    In Node, the map is the territory: if you export anything to module.exports, you're a module.

    I know I'm hairsplitting here, but in my head I wasn't considering it a node module because I'm not adding anything to module.exports, and I'm not adding it to the install location's packages.json (which is what I originally worried NodeBB was checking).

    @anotherusername said:

    To actually make it order-independent, you wouldn't be able to do it in pure CSS... you'd need to select .post-tools and then use Javascript to filter out any that contain a.viewRaw descendant nodes.

    You can't do it in pure CSS, but you can use JQuery's selectors to do something like '.post-tools:not(:has(a.view-raw))'. I don't want to think about the complexity of that, but it will filter out any .post-tools that already contain a.view-raw.

    Edit: :hanzo: by @accalia.

    Although does it count as a :hanzo: if I checked it in last night?


Log in to reply