0

I'm in the middle of making a Chrome extension that adds a 'Shorten URL' button to the context menu. This is the first time I've ever done anything code related, but I'm doing ok.

At the moment I've got my button coming up whenever you highlight text (using selectionText). What I want it to do is only come up if the text is a 'text' URL (i.e. plain text that's a URL, but not a link).

I know this is possible, because Google do it. If you highlight and right-click on text a 'Search Google for' button comes up, but if you highlight and right-click a plain text URL a 'Go to' button comes up.

How can I do this in my extension?

Thank you.

Edit:

I've tried to get it working in my extension using Timothée Boucher's advice, but I must be doing something wrong (see my comment below).

Here's what I've got so far:

background.js

function shortenurl1(info) {
    var searchstring = info.pageUrl;
    chrome.tabs.create({
        url: "http://is.gd/create.php?url=" + searchstring
    })
}

chrome.contextMenus.create({
    title: "Shorten URL (with is.gd)",
    contexts: ["page"],
    onclick: shortenurl1
});

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (request.action === 'addContextMenu') {
            function shortenurl2(info) {
                var searchstring = info.selectionText;
                chrome.tabs.create({
                    url: "http://is.gd/create.php?url=" + searchstring
                })
            }

            chrome.contextMenus.create({
                title: "Shorten URL (with is.gd)",
                contexts: ["selection"],
                onclick: shortenurl2
            })
        } else if (request.action === 'removeContextMenu') {
            chrome.contextMenus.remove({
                title: "Shorten URL (with is.gd)"
            })
        }
    }
);

function shortenurl3(info) {
    var searchstring = info.linkUrl;
    chrome.tabs.create({
        url: "http://is.gd/create.php?url=" + searchstring
    })
}

chrome.contextMenus.create({
    title: "Shorten URL (with is.gd)",
    contexts: ["link"],
    onclick: shortenurl3
});

contentscript.js

document.onmouseup = function() {
    var selectedText = window.getSelection().toString();
    // this regex should work for very basic cases but doesn't follow the
    // RFC strictly for what constitutes a URL. Full regex left as exercise ;-)
    if (/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-    ;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:    [\w]*))?)/.test(selectedText)) {
        // send a message to the background page to add the context menu
        chrome.extension.sendMessage({action: 'addContextMenu', url: selectedText});
    } else {
        // send a message to the background page to remove the context menu
        chrome.extension.sendMessage({action: 'removeContextMenu'});
    }
};

manifest.json

{
    "background": {
        "scripts": ["background.js"]
    },
    "content_scripts": [{
        "run_at": "document_idle",
        "js": ["contentscript.js"],
        "matches": ["<all_urls>"]
    }],
    "description": "Shortens URLs using is.gd.",
    "icons": {
        "16": "icon16.png",
        "48": "icon48.png"
    },
    "name": "is.gd context menu",
    "permissions": ["contextMenus", "tabs", "clipboardWrite"],
    "version": "1.0",
    "manifest_version": 2
}
Sasidhar Vanga
  • 3,384
  • 2
  • 26
  • 47
Josey
  • 25
  • 5
  • "How can I do this in my extension" --> Look at [Example 1](http://stackoverflow.com/a/15304431/938089 "Showing context menu buttons only when right-clicked on classes that start with 'Story'") and [Example 2](http://stackoverflow.com/questions/13202896/creating-dynamic-context-menu-in-chrome-extension-is-failing "Creating dynamic context menu in Chrome Extension is failing") – Rob W Aug 16 '13 at 16:38

1 Answers1

0

As far as I know when you create the context menu in advance (e.g. when your extension is loaded) for that purpose, it'll be either/or. Meaning that you'll get it for all selected text or not at all. So you'll have to add and remove it dynamically.

You can do the following:

  1. In a content script, add a listener on document.onmouseup.
  2. When it's called, check the content of the currently selected text.
  3. If it matches a regular expression for a URL, have the background page add a context menu, otherwise remove the context menu.
  4. There is no step 4.

So in your content script, you could have something like that:

document.onmouseup = function() {
    var selectedText = window.getSelection().toString();
    // this regex should work for very basic cases but doesn't follow the
    // RFC strictly for what constitutes a URL. Full regex left as exercise ;-)
    if (/^\s*https?:\/\/[a-zA-Z0-9$-_.+!*'(),]+\s*$/.test(selectedText)) {
        // send a message to the background page to add the context menu
        chrome.extension.sendMessage({action: 'addContextMenu', url: selectedText});
    } else {
        // send a message to the background page to remove the context menu
        chrome.extension.sendMessage({action: 'removeContextMenu'});
    }
};

And in your background script, listen for messages and create and remove the context menu accordingly.

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        if (request.action === 'addContextMenu') {
            // code to add the context menu
            // You can access the URL in 'request.url'
        } else if (request.action === 'removeContextMenu') {
            // Remove the context menu
        }
    }
);

See this page for information on message passing.

NB: you might be able to create the menu from the content script but I haven't tried.

Timothée Boucher
  • 1,535
  • 12
  • 30
  • Thank you. I have two questions. Can I put any message in the (...) bits (for example 'itisaurl' and 'itsnotaurl')? And How do I get my background script to listen for it? Is is using something like 'chrome.extension.onMessage.addListener', and then could I use 'if (itisaurl)' and 'if (itsnotaurl)'? – Josey Aug 16 '13 at 16:41
  • Yes, see [this page](http://developer.chrome.com/extensions/messaging.html#simple). Basically, you can send whatever you want in `chrome.runtime.sendMessage`. See [the doc](http://developer.chrome.com/extensions/runtime.html#method-sendMessage) for it. I usually send an object with an `action` attribute. You could do `{action: 'addContextMenu', url: selectedText}` and `{action: 'removeContextMenu'}`. Then in the background page you can test for what is in `action`. Adding this to the answer. – Timothée Boucher Aug 16 '13 at 16:49
  • Well, I must be doing something wrong. It looks like the massages are taking too long. At first my button isn't there when I select any text (including text URLs) and right-click, and then after a while the button is always there when I select any text and right-click. And a bit later copies of the button start popping up, so that after about 5 minutes there's a parent menu with 9 or so buttons in it. I'll add the code to my question. – Josey Aug 16 '13 at 18:13
  • The thing is that we never check if a context menu is already there or not, so if you select a URL twice in a row, you will be adding a context menu twice. So the background script needs to check if a context menu is already there before adding or removing it. Also, I just realized that the code I put probably needs to check if it's left or right-click. I'm not sure but it's likely that `onmouseup` will be triggered on both left and right click which might mess up with some stuff. See http://stackoverflow.com/questions/1206203/how-to-distinguish-between-left-and-right-mouse-click-with-jquery – Timothée Boucher Aug 16 '13 at 18:19
  • Thanks. I'll try again in a few days when I have some time and see if I can get this to work. – Josey Aug 17 '13 at 00:05