1

I'm attempting to replace url's in a string by calling a function that shortens the url's and then replaces the text. The shortening logic works perfectly, and I end up with the proper replacement url, however i cannot get the replace function to work correctly. Here is my code.

var newtext = shortenUrl(curText);

var shortenUrl = function (text) {
    var exp = /(ftp|http|https):\/\/(?!bit\.ly)(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ig;

    text.replace(exp, 
                 function ($1) {
                      $.getJSON("http://api.bitly.com/v3/shorten?login=xxxxxx&apiKey=xxxx&longUrl=" + escape($1) + "&format=json", 
                                function (result) {
                                    return result.data.url;
                                });
                 });
    return text;
};

result.data.url contains a proper shortened url per the bit.ly API specs; however, newtext is always null for some reason.

Matt Phillips
  • 11,249
  • 10
  • 46
  • 71
  • Try making your `shortenUrl` a function (not anonymous). – Blender Nov 23 '11 at 04:02
  • I just dealt with a problem like this a little while ago. See this question: http://stackoverflow.com/questions/8196889/still-having-problems-returning-value-through-function – rossipedia Nov 23 '11 at 04:02

2 Answers2

4
  1. text.Replace will not change the contents of text => you need to say text = text.Replace(...) because strings in javascript are immutable (https://stackoverflow.com/q/51193/4572 for more info)

  2. getJSON is performed asynchronously meaning that execution of the function that you feed to replace will start the JSON call but doesn't wait for the JSON call to finish , you need to use $.ajax instead with the async option turned off (see the code below, as well as this link: Is it possible to set async:false to $.getJSON call)

  3. Because the JSON call merely executes a callback (ie function(result)) it doesn't actually return a value so you need to create what is called a "closure" in order to get state (result.data.url) from inside your function(result) call to the parent scope (in this case the scope created by function($1)). Closures can get very confusing, especially in javascript, sometimes yielding non-obvious results. Thankfully this is a simple closure. For a little more info you can checkout this https://stackoverflow.com/q/7363168/4572, or the google.

Here is the code that works correctly for me:

var shortenUrl = function (text) {
    var exp = /(ftp|http|https):\/\/(?!bit\.ly)(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ig;

    text = text.replace(exp, function ($1) {
       var newUrl = "";
       $.ajax({
           url: "http://api.bitly.com/v3/shorten?login=xxxxx&apiKey=xxxxxx&longUrl=" + escape($1) + "&format=json",
           dataType: 'json',
           async: false,
            success: function(result) {
                newUrl = result.data.url
            }
        });
        return newUrl;
    });
    return text;
};
Community
  • 1
  • 1
viggity
  • 15,039
  • 7
  • 88
  • 96
0

As the functions you are calling are not synchronous you will need some sort of callback function. The following code should do the trick:

var newtext;
shortenUrl(curText, function(url) {
  newtext = url;
});

var shortenUrl = function (text, callback) {
    var exp = /(ftp|http|https):\/\/(?!bit\.ly)(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ig;

    text = text.replace(exp, 
             function ($1) {
                  $.getJSON("http://api.bitly.com/v3/shorten?login=xxxxxx&apiKey=xxxx&longUrl=" + escape($1) + "&format=json", 
                            function (result) {
                                return result.data.url;
                            });
             });
    callback(text);
};
Michael Leaney
  • 753
  • 4
  • 10
  • I like this approach except it replaces all of the string with the bitly link and not just the url. – Matt Phillips Nov 23 '11 at 04:16
  • Sorry about that, I misunderstood the code. You should find all urls in the string, and then reduce them to only the unique ones. You should then fire a request to shorten each url and store the result, once ALL of the requests are done, you will need to call the callback with the shortened urls replacing the full length ones. Add a bounty and I'll be sure to do the code for you sometime today :) – Michael Leaney Nov 23 '11 at 04:48
  • sorry bud. One of my friends was lookin out for me on this one! Thank you for your efforts though. Obviously I have much to learn about javascript! – Matt Phillips Nov 23 '11 at 04:56