21

Screenshot / Code Snippet

enter image description here

About

This small UserScript makes it easier to copy the URL of the current question into pre-formatted Markdown. Once installed, it adds a 'copy' link to the question.

License

MIT License

Download

http://files.quickmediasolutions.com/userscripts/copy.user.js

Platform

Any browser that can install UserScripts.

Contact

I can be reached at [email protected]

Code

JavaScript + jQuery

Source

// ==UserScript==
// @name          Copy Question Link Markdown
// @author        Nathan Osman
// @namespace     http://quickmediasolutions.com
// @description   Adds a button for copying the markdown of a question's URL to the clipboard
// @include       http://stackoverflow.com/*
// @include       http://meta.stackoverflow.com/*
// @include       http://superuser.com/*
// @include       http://serverfault.com/*
// @include       http://meta.superuser.com/*
// @include       http://meta.serverfault.com/*
// @include       http://stackapps.com/*
// @include       http://askubuntu.com/*
// @include       http://*.stackexchange.com/*
// ==/UserScript==

// Here I borrow a couple functions I wrote for another
// UserScript that makes it easy to provide functions
// with complete access to the page.
function EmbedFunctionOnPageAndExecute(function_contents)
{
    var exec_script = document.createElement('script');
    exec_script.type = 'text/javascript';
    exec_script.textContent = "(" + function_contents.toString() + ")()";
    document.getElementsByTagName('head')[0].appendChild(exec_script);
}

// ...the other one
function EmbedFunctionOnPage(function_name, function_contents)
{
    var exec_script = document.createElement('script');
    exec_script.type = 'text/javascript';
    exec_script.textContent = function_contents.toString().replace(/function ?/, 'function ' + function_name);
    document.getElementsByTagName('head')[0].appendChild(exec_script);
}

// The code to execute when the ZeroClipboard lib loads
EmbedFunctionOnPage('DoCopy', function() {
    
    // Determine the content of the DIV
    var content = '<b>Copy the following:</b><br /><br /><textarea id="copy-content" style="width: 440px; height: 80px;">[' +
                  $('#question-header .question-hyperlink').text() + '](' + location.href + ')</textarea>';
    
    $('body').append('<div id="copy-popup" class="popup" style="width: 450px; position: absolute; display: none;">' +
                     '<div class="popup-close"><a title="close this popup">×</a></div>' + content + '</div>');
    
    // Assign the close action to the 'x'
    $('#copy-popup .popup-close').click(function() { $('#copy-popup').fadeOut(300, function() { $('#copy-popup').remove(); }); });
    
    // Position the dialog
    var pos    = $('#copy-button').position();
    var height = $('#copy-button').height();
    
    $('#copy-popup').css('left', pos.left);
    $('#copy-popup').css('top', pos.top + height + 3);
    
    $('#copy-popup').fadeIn(500, function() {
        
        // Select the contents
        var textarea = document.getElementById('copy-content');
    
        textarea.focus();
        textarea.select();
    
    });
    
});

// The code that gets executed right away.
EmbedFunctionOnPageAndExecute(function() {
    
    // Embed the copy item
    $('.post-menu').append("<span class='lsep'>|</span><a href='javascript:void(0)' onclick='DoCopy()' id='copy-button'>copy</a>");
    
});
11
  • When I copy the link to an answer, the box appears near the first "copy" link (the one under the question). This behavior is pretty annoying. It should appear next to the "copy" link that triggered displaying the box. Commented Mar 1, 2011 at 19:29
  • @Klaus: Wow - it wasn't even designed to display a copy link next to answers in the first place. I'll try to get that fixed soon. Commented Mar 1, 2011 at 19:34
  • 2
    +1 sweet! This is a good workaround for the issue I raise here. meta.stackoverflow.com/questions/89378/… thanks for this!
    – Pekka
    Commented Apr 30, 2011 at 15:58
  • Ideally, this should copy the personalised (and shorter!) link instead of the full canonical link. Commented May 16, 2012 at 16:18
  • Copying the link for closed questions inserts [closed] into the Markdown link title, which isn't valid. Ideally, it should be simply stripped .
    – slhck
    Commented Sep 10, 2012 at 17:26
  • 3
    The link is down, could you update it?
    – slhck
    Commented Apr 15, 2014 at 19:39
  • I created a script that changes the very share link shown in the dialog: Convert share links to markdown [title](url)
    – brasofilo
    Commented Sep 29, 2014 at 14:54
  • @slhck: I've restored the original file. Commented Dec 24, 2014 at 19:14
  • will it auto copy for me when I click the copy button? No need to Ctrl+C.
    – Ooker
    Commented Jan 31, 2015 at 9:07
  • This is pretty sweet. I've got a similar script here if you want more options, at the expense of a larger share link dialog.
    – Jason C
    Commented Jun 1, 2017 at 17:29
  • Doesn't work, TypeError: lastFooterLink is undefined error in the console. developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/…
    – user598527
    Commented Jul 9, 2017 at 18:56

2 Answers 2

5

Thank you for this script. It was basically the thing I was looking for when I asked for a Second 'link' button below posts which provides a comment style link in the form [post title](http://posturl).

There are however some things not yet perfect. For once I also like to have such links to answers not just to question. In this case the question title is still used but the URL of the answer should be used. At the moment every post (Qs and As) get the 'copy' link (all with the same id, which is invalid HTML), but the pop-up window only appears beside the question.

Also, the short URL should be used to allow for more additional text in comments.


I took the liberty to add these features to your code. Feel free to take over and adapt any changes. I added (modified) to the @name and renamed it to copy2.user.js to allow both versions to be installed at the same time (with one usually disabled).

Code preview: http://www.scharrer-online.de/attachment/wiki/Software/files/copy2.user.js

Installation link: http://www.scharrer-online.de/raw-attachment/wiki/Software/files/copy2.user.js

// ==UserScript==
// @name          Copy Question Link Markdown (modified)
// @author        Nathan Osman
// @author        Martin Scharrer
// @namespace     http://quickmediasolutions.com
// @description   Adds a button for copying the markdown of a post's URL to the clipboard
// @include       http://stackoverflow.com/*
// @include       https://meta.stackoverflow.com/*
// @include       http://superuser.com/*
// @include       http://serverfault.com/*
// @include       http://meta.superuser.com/*
// @include       http://meta.serverfault.com/*
// @include       http://stackapps.com/*
// @include       http://askubuntu.com/*
// @include       http://*.stackexchange.com/*
// ==/UserScript==

/* Modified by Martin Scharrer 14th April 2012 to support links to answers and to use short form URLs.
 *
 * Changelog:
 *  o Added support for answer posts.
 *    - The pop-up window now appears beside the clicked 'copy' button, not at the very first one.
 *    - The included URL will point to the post the button belongs to, not always to the question.
 *  o The short version of the URL is now used to minimize the number of characters.
 *  o Changed "id='copy-button'" to "class='copy-button'" to allow multiple buttons.
 *    Before all posts got the button, which incorrectly had all the same id.
 *  o The object of the 'copy' button is now passed along with the click, not determined by its id.
 * */

// Here I borrow a couple functions I wrote for another
// UserScript that makes it easy to provide functions
// with complete access to the page.
function EmbedFunctionOnPageAndExecute(function_contents)
{
    var exec_script = document.createElement('script');
    exec_script.type = 'text/javascript';
    exec_script.textContent = "(" + function_contents.toString() + ")()";
    document.getElementsByTagName('head')[0].appendChild(exec_script);
}

// ...the other one
function EmbedFunctionOnPage(function_name, function_contents)
{
    var exec_script = document.createElement('script');
    exec_script.type = 'text/javascript';
    exec_script.textContent = function_contents.toString().replace(/function ?/, 'function ' + function_name);
    document.getElementsByTagName('head')[0].appendChild(exec_script);
}

// The code to execute when the ZeroClipboard lib loads
EmbedFunctionOnPage('DoCopy', function(button) {
    // Get ids required to build short URL to the post
    var questionid = location.path.match(/\/questions\/(\d+)\/.*/)[1];
    var shortquestionpath = $("#link-post-" + questionid).attr("href");
    var userid = shortquestionpath.substr( questionid.length + 4 ); // "/q/<questionid>/<userid>"
    var answerid = $(button).parents("div.answer").log("answer").attr("data-answerid");
    var shorturl;
    if (answerid) {
        shorturl = "http://" + location.host + "/a/" + answerid + "/" + userid;
    }
    else {
        shorturl = "http://" + location.host + shortquestionpath;
    }

    // Determine the content of the DIV
    var content = '<b>Copy the following:</b><br /><br /><textarea id="copy-content" style="width: 440px; height: 80px;">[' +
                  $('#question-header .question-hyperlink').text() + '](' + shorturl + ')</textarea>';

    $('body').append('<div id="copy-popup" class="popup" style="width: 450px; position: absolute; display: none;">' +
                     '<div class="popup-close"><a title="close this popup">×</a></div>' + content + '</div>');

    // Assign the close action to the 'x'
    $('#copy-popup .popup-close').click(function() { $('#copy-popup').fadeOut(300, function() { $('#copy-popup').remove(); }); });

    // Position the dialog
    var pos    = $(button).position();
    var height = $(button).height();

    $('#copy-popup').css('left', pos.left);
    $('#copy-popup').css('top', pos.top + height + 3);

    $('#copy-popup').fadeIn(500, function() {

        // Select the contents
        var textarea = document.getElementById('copy-content');

        textarea.focus();
        textarea.select();

    });

});

// The code that gets executed right away.
EmbedFunctionOnPageAndExecute(function() {

    jQuery.fn.log = function (msg) {
    console.log("%s: %o", msg, this);
    return this;
    };
    // Embed the copy item
    $('.post-menu').append("<span class='lsep'>|</span><a href='javascript:void(0)' onclick='DoCopy(this)' class='copy-button'>copy</a>");

});
0

This script is now unnecessary in questions and answers, where raw links are replaced by the current question title. However, this feature is [status-declined] for comments and doesn't work in Chat, which also uses mini-markdown. Chat does have oneboxing, but you can't insert names inline without this script or a bunch of typing or copy-pasting.

If my terrible JavaScript skills are correct, you're currently using location.href to determine the page. This text is something like:

https://meta.stackoverflow.com/questions/89378/userscript-to-auto-replace-sofu-links-in-comments

That's 96 characters, a fairly long URL (but not maximally long, that's over 120 characters!). Unfortunately, the places where this script is necessary and useful (chat and comments) both have character limits.

Please change this to use the shorter https://meta.stackoverflow.com/q/89378 and https://meta.stackoverflow.com/q/89378/89389#89389 format. That seems like it would be a trivial Javascript string manipulation. Here's an attempt, I make no assumptions as to its validity, speed, or idiomatic-ness:

function minify(url) {

    var question_re = /(^\D+)question\/(\d+)/;

    var answer_re = /\/\d+#\d+$/;
    if (re.test(answer_re)) {
       return url.replace(question_re, '$1q$2') .. url.replace(answer_re);
    } else {
       return url.replace(qestion_re, '$1q$2');
    }
}

and a testcase.

function AssertException(message) { this.message = message; }
AssertException.prototype.toString = function () {
  return 'AssertException: ' + this.message;
}

function assert_equal(expression, result) {
  if (expression != result) {
    throw new AssertException('Expression produced "' .. expression .. '" which did not equal "' .. result .. '".');
  }
}

 // Test function:
function verify_minify()
{

    assert_equal('test_str',
                 'test_str');

    // Standard Trilogy question
    assert_equal(minify('http://stackoverflow.com/question/12345/some-text'),
                      'http://stackoverflow.com/q/12345');
    //With answer ID:
    assert_equal(minify('http://stackoverflow.com/q/12345/some-text/12346#12346'),
                      'http://stackoverflow.com/q/12345/12346#12346');
    //With Meta prefix:
    assert_equal(minify('https://meta.stackoverflow.com/question/12345/some-text'),
                      'https://meta.stackoverflow.com/q/12345');
    //With Meta prefix and answer ID:
    assert_equal(minify('https://meta.stackoverflow.com/q/12345/some-text/12346#12346'),
                      'https://meta.stackoverflow.com/q/12345/12346#12346');
    //With site.stackexchange format: 
    assert_equal(minify('http://electronics.stackexchange.com/q/12345/some-text'),
                      'http://electronics.stackexchange.com/q/12345');
    //With site.stackexchange format and answer ID: 
    assert_equal(minify('http://electronics.stackexchange.com/q/12345/some-text/12346#12346'),
                      'http://electronics.stackexchange.com/q/12345/12346#12346');
    //With site.stackexchange format, meta prefix, and answer ID: 
    assert_equal(minify('http://meta.electronics.stackexchange.com/q/12345/some-text/12346#12346'),
                      'http://meta.electronics.stackexchange.com/q/12345/12346#12346');

    alert('Tests passed.');

}

Note: I didn't actually run any of these except to verify that the syntax highlighter in my text editor didn't break, I don't have any kind of a development environment set up and don't know how to do that.

1
  • I'll see what I can do - I didn't realize there was still a need for this script. Commented Oct 26, 2011 at 17:32

You must log in to answer this question.

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