18
\$\begingroup\$

One of the more popular comments on our site goes something like this:

Hi, and welcome to PPCG! Your challenge was closed because it's a bit unclear/broad/whatever as it is now. I would recommend posting in the Sandbox so you can get constructive feedback before you post to main.

(Please excuse the sloppy wording, but we've all seen various examples of this comment)

When I'm at a PC, this is fairly simple. On mobile, while it's not exactly impossible, it's definitely more of a hassle. I usually just don't bother and hope someone else does. Even while at a PC though, it would be nice to not have to open another tab to grab the link.

The solution for many commonly commented links is a "magic link", which would automatically be converted to a proper link when posted. I think [sandbox] would be a good addition. It's probably the most common link on the whole site (in comments, that is), so it would simplify writing these introductions.

Now, I don't know how SE handles these things, or whether they even want to do custom links for each site, but I think it's worth proposing here first to gauge interest.

\$\endgroup\$
  • \$\begingroup\$ Possible duplicate of We're not a Q&A site. But what should be done about it? \$\endgroup\$ – noɥʇʎԀʎzɐɹƆ Dec 8 '16 at 22:06
  • 2
    \$\begingroup\$ I thought about adding it there, but I don't believe it has much to do with whether or not we're a strict Q/A site. \$\endgroup\$ – Geobits Dec 8 '16 at 22:07
  • \$\begingroup\$ A tentative choice for the close voters... \$\endgroup\$ – noɥʇʎԀʎzɐɹƆ Dec 8 '16 at 22:08
  • 10
    \$\begingroup\$ Tentative? If you don't know whether or not you should close it, let others decide. There's no reason to tentatively cast a vote. \$\endgroup\$ – Geobits Dec 8 '16 at 22:15
  • \$\begingroup\$ Related request on mother Meta \$\endgroup\$ – DJMcMayhem Dec 8 '16 at 22:16
  • 1
    \$\begingroup\$ @CrazyPython That question should be closed as too broad, and you just demonstrated exactly why. \$\endgroup\$ – Rainbolt Dec 8 '16 at 22:17
  • \$\begingroup\$ @DJMcMayhem Ah, I must have overlooked that in my search somehow. While the lack of response is disheartening, at least there's not a definite "no" from the dev team. \$\endgroup\$ – Geobits Dec 8 '16 at 22:19
  • 1
    \$\begingroup\$ @CrazyPython in support of it not being about whether we're a Q&A site, several of the Q&A sites have a sandbox too. For example, worldbuilding's sandbox and math's sandbox (confusingly, that one is closed but still in active use). \$\endgroup\$ – trichoplax Dec 8 '16 at 22:26
  • 3
    \$\begingroup\$ While we're at it, I think magic links for standard loopholes and things to avoid when writing challenges would be nice. \$\endgroup\$ – DJMcMayhem Dec 8 '16 at 22:47
  • \$\begingroup\$ @DJMcMayhem Yeah. The main reason I didn't include those is that I usually link to a specific answer when linking those, not the question in general. \$\endgroup\$ – Geobits Dec 8 '16 at 22:52
  • \$\begingroup\$ We just need a magic link for each answer... \$\endgroup\$ – trichoplax Dec 8 '16 at 22:55
  • \$\begingroup\$ Hold up... I'm using a magic link! [meta] Open Source Code Golf help center \$\endgroup\$ – Zizouz212 Dec 8 '16 at 23:01
  • \$\begingroup\$ @Zizouz212 the magic doesn't appear to be working... \$\endgroup\$ – trichoplax Dec 8 '16 at 23:27
  • \$\begingroup\$ @trichoplax I'm confused... \$\endgroup\$ – Zizouz212 Dec 8 '16 at 23:28
  • \$\begingroup\$ @Zizouz212 for your previous comment I see "Hold up... I'm using a magic link! [meta] Open Source Programming Puzzles & Code Golf help center " - all the magic links seem to be working apart from [meta]. Maybe [meta] only works from main... \$\endgroup\$ – trichoplax Dec 8 '16 at 23:30
-1
\$\begingroup\$

There's a super cool user script that I use to create auto comments, such as this one on Stack Overflow.

Yaar. Now where did I find it... Oh. Doorknob made it. I'll just copy the script since I don't have the link handy. You can set custom comments to your desire. I think it uses jQuery too!

I doubt SE would do something for the site, since it's very site specific, but hey. I don't know.

// ==UserScript==
// @name stackexchange-quickcomment2
// @namespace http://keyboardfire.com/
// @grant none
// @license MIT
// @description Quick SE comments for quick SE people. (Vastly improved)
// @version 1.0.0
// @match *://*.stackexchange.com/*
// @match *://*.stackoverflow.com/*
// @match *://*.superuser.com/*
// @match *://*.serverfault.com/*
// @match *://*.askububtu.com/*
// @match *://*.stackapps.com/*
// @match *://*.mathoverflow.net/*
// ==/UserScript==

var userscript = function($) {

var qc2;
function updateLS() { localStorage.qc2 = JSON.stringify(qc2); }
if (localStorage.qc2) {
    qc2 = JSON.parse(localStorage.qc2);
} else {
    qc2 = {
        trigger: {
            altKey: true,
            ctrlKey: false,
            shiftKey: false,
            which: 88
        },
        data: {
            naa: 'This is not an answer.',
            welcome: 'Welcome to $SITENAME!',
            lorem: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
        }
    };
    updateLS();
}

$(function() {
    $('<style>' +
        '.qc2_selected { background-color: #FF0; }' +
        '.qc2_popup { background-color: #E0EAF1; text-align: left; }' +
        '.qc2_popup > * { margin: 5px; word-wrap: break-word; }' +
    '</style>').appendTo('head');

    var dialog = $('<div>')
        .attr('id', 'qc2_dialog')
        .appendTo(document.body)
        .css({
            position: 'absolute',
            width: '250px',
            height: '350px',
            textAlign: 'left',
            overflowX: 'hidden',
            overflowY: 'scroll'
        })
        .addClass('qc2_popup')
        .append($('<button>')
            .text('settings')
            .mousedown(showSettings)
        )
        .append($('<input>')
            .attr('type', 'text')
            .keydown(function(e) {
                switch (e.which) {
                case 27: // esc
                    hideDialog();
                    break;
                case 40: // down arrow
                    e.preventDefault();
                    var toChange = $('.qc2_selected').nextAll('div:visible').eq(0);
                    if (toChange.length) {
                        $('.qc2_selected').removeClass('qc2_selected');
                        toChange.addClass('qc2_selected');
                    }
                    break;
                case 38: // up arrow
                    e.preventDefault();
                    var toChange = $('.qc2_selected').prevAll('div:visible').eq(0);
                    if (toChange.length) {
                        $('.qc2_selected').removeClass('qc2_selected');
                        toChange.addClass('qc2_selected');
                    }
                    break;
                }
            })
            .keyup(function(e) {
                if (e.which === 13) { // enter
                    var cmt = dialog.data('comment');
                    var str = $('span:last', '.qc2_selected').text()
                        .replace(/\$SITENAME/g, StackExchange.options.site.name)
                        .replace(/\$SITEURL/g, window.location.hostname);
                    cmt.val(cmt.val() + str);
                    hideDialog();
                } else if (e.which !== 40 && e.which !== 38) {
                    var txt = $('input', dialog).val(),
                        needToUpdateSelected = !$('>div:visible').length;
                    $('>div span:first-child', dialog).each(function() {
                        if (this.textContent.indexOf(txt) === -1) {
                            $(this).parent().hide();
                            if ($(this).parent().hasClass('qc2_selected')) {
                                needToUpdateSelected = true;
                            }
                        } else {
                            $(this).parent().show();
                        }
                    });
                    if (needToUpdateSelected) {
                        $('.qc2_selected').removeClass('qc2_selected');
                        $('>div:visible:first', dialog).addClass('qc2_selected');
                    }
                }
            })
            .blur(hideDialog)
        )
        .hide();

    // I don't know why I have to do this
    // but it works, so I'll just go with it
    dialog[0].scrollTop = 0;

    reloadDialogData();

    $(document).on('keydown', 'textarea[name="comment"]', function(e) {
        if (qc2.trigger.altKey === e.altKey &&
            qc2.trigger.ctrlKey === e.ctrlKey &&
            qc2.trigger.shiftKey === e.shiftKey &&
            qc2.trigger.which === e.which) {
            e.preventDefault();
            showDialog($(this));
        }
    });
});

function showDialog(commentBox) {
    $('#qc2_dialog').data('comment', commentBox).css({
        top: commentBox.offset().top,
        left: commentBox.offset().left
    }).show('fast');
    $('#qc2_dialog input').focus();
}

function hideDialog() {
    $('#qc2_dialog>div').show().removeClass('qc2_selected');
    $('#qc2_dialog>div:first').addClass('qc2_selected');
    $('#qc2_dialog input').val('');
    $('#qc2_dialog').hide().data('comment').focus();
}

function reloadDialogData() {
    $('#qc2_dialog>div').remove();
    for (var k in qc2.data) {
        var v = qc2.data[k];
        $('#qc2_dialog').append(kvpair(k, v));
    }
    $('#qc2_dialog>div:first').addClass('qc2_selected');
}

function showSettings() {
    var settingsDiv = $('<div>')
        .css({
            position: 'fixed',
            top: '50%',
            left: '50%',
            width: '400px',
            height: '400px',
            margin: '-200px 0px 0px -200px',
            overflowX: 'hidden',
            overflowY: 'scroll'
        })
        .addClass('qc2_popup')
        .appendTo(document.body)
        .append($('<button>')
            .text('Done')
            .click(function() {
                settingsDiv.remove();
            })
        )
        .append($('<br>'))
        .append($('<label>')
            .attr('for', 'qc2_trigger_input')
            .text('Trigger:')
        )
        .append($('<input>')
            .attr('id', 'qc2_trigger_input')
            .attr('type', 'text')
            .val(triggerStr())
            .keydown(function(e) {
                e.preventDefault();
                qc2.trigger = {
                    altKey: e.altKey,
                    ctrlKey: e.ctrlKey,
                    shiftKey: e.shiftKey,
                    which: e.which
                };
                updateLS();
                $(this).val(triggerStr());
            })
        );

    for (var k in qc2.data) {
        var v = qc2.data[k];
        settingsDiv.append(kvpair(k, v)
            .append(linkbtn('edit', function(e) {
                    settingsDiv.remove();
                    var editKey = $(e.target).siblings(':first').text();
                    getStr('New value for ' + editKey + ':', function(str) {
                        qc2.data[editKey] = str;
                        updateLS();
                        reloadDialogData();
                        showSettings();
                    }, qc2.data[editKey]);
                })
            )
            .append(linkbtn('delete', function(e) {
                    settingsDiv.remove();
                    var editKey = $(e.target).siblings(':first').text();
                    getStr('Are you sure you want to remove ' + editKey +
                        '? (yes/no)', function(str) {
                        if (str === 'yes') {
                            delete qc2.data[editKey];
                            updateLS();
                            reloadDialogData();
                        }
                        showSettings();
                    });
                })
            )
        );
    }

    settingsDiv.append(linkbtn('new...', function() {
            settingsDiv.remove();
            getStr('New shortcut key:', function(newKey) {
                getStr('New value for ' + newKey + ':', function(newVal) {
                    qc2.data[newKey] = newVal;
                    updateLS();
                    reloadDialogData();
                    showSettings();
                });
            });
        })
    )
    .append(linkbtn('export...', function() {
            settingsDiv.remove();
            getStr('Copy the following text:', function() {}, JSON.stringify(qc2));
        })
    )
    .append(linkbtn('import...', function() {
            settingsDiv.remove();
            getStr('Paste exported text here:', function(str) {
                qc2 = JSON.parse(str);
                updateLS();
                reloadDialogData();
            });
        })
    );
}

function triggerStr() {
    return (
        (qc2.trigger.altKey ? 'Alt+' : '') +
        (qc2.trigger.ctrlKey ? 'Ctrl+' : '') +
        (qc2.trigger.shiftKey ? 'Shift+' : '') +
        String.fromCharCode(qc2.trigger.which)
    );
}

function getStr(query, callback, placeholder) {
    if (!placeholder) placeholder = '';
    var getDiv = $('<div>')
        .css({
            position: 'fixed',
            top: '50%',
            left: '50%',
            width: '400px',
            marginLeft: '-200px'
        })
        .addClass('qc2_popup')
        .appendTo(document.body)
        .append($('<div>')
            .text(query)
        )
        .append($('<input>')
            .val(placeholder)
            .attr('type', 'text')
            .keydown(function(e) {
                if (e.which === 13) {
                    $(this).parent().remove();
                    callback($(this).val());
                }
            })
        );
    getDiv.css({
        height: getDiv.height() + 'px',
        marginTop: '-' + (getDiv.height() / 2) + 'px'
    });
    $('input', getDiv).focus().select();
}

function linkbtn(text, callback) {
    return $('<a>')
        .attr('href', '#')
        .text(text)
        .css('padding-left', '5px')
        .click(function(e) {
            e.preventDefault();
            callback(e);
        });
}

function kvpair(k, v) {
    return $('<div>')
        .css({
            borderBottom: '1px dotted grey',
            padding: '2px 0px'
        })
        .append($('<span>')
            .text(k)
            .css({
                fontWeight: 'bold',
                paddingRight: '5px'
            })
        )
        .append($('<span>')
            .text(v)
            .css({
                color: 'grey'
            })
        );
}

};

var el = document.createElement('script');
el.type = 'text/javascript';
el.text = '(' + userscript + ')(jQuery);';
document.head.appendChild(el);

So let's see:

  • Desktops/Laptops/Whatevertops................. YES!
  • Android............................................................. YES!
  • iOS..................................................................... Who knows.

Thanks to Nathan Merrill for talking about Android

\$\endgroup\$
  • 6
    \$\begingroup\$ That doesn't really address the inconvenience of mobile... \$\endgroup\$ – DJMcMayhem Dec 8 '16 at 22:46
  • \$\begingroup\$ @DJMcMayhem If you use mobile, then you're screwed. It is a solution for people on laptops/desktops/what-tops. But who knows. Maybe you can add a user script to mobile... You folks are all pretty darn smart around here \$\endgroup\$ – Zizouz212 Dec 8 '16 at 22:47
  • \$\begingroup\$ Installing a separate mobile browser for use on SE is more inconvenient, not less. \$\endgroup\$ – Geobits Dec 8 '16 at 23:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .