rangy.createModule('SafeWrapLink', function(api, module) {
  var surroundSelectionWithLink;
  surroundSelectionWithLink = (function(href) {
    var after, afterLink, afterLinkHref, before, beforeLink, beforeLinkHref, currentLink, fullText, link, par, parNode, range, selectionText;
    range = document.getSelection().getRangeAt(0);
    selectionText = range.toString();
    if (range.commonAncestorContainer.nodeName !== "#text") {
      beforeLinkHref = range.commonAncestorContainer.childNodes[0].getAttribute('href');
      afterLinkHref = range.commonAncestorContainer.childNodes[2].getAttribute('href');
      par = range.commonAncestorContainer;
      parNode = par;
    } else {
      par = range.commonAncestorContainer.parentNode;
      currentLink = par.getAttribute('href');
      parNode = par.parentNode;
    }
    fullText = par.innerText;
    before = fullText.match(new RegExp("^(.*)" + selectionText))[1];
    after = fullText.match(new RegExp(selectionText + "(.*)$"))[1];
    
    // Build link for before selection
    beforeLink = document.createElement('a');
    beforeLink.href = beforeLinkHref || currentLink;
    beforeLink.innerText = before;
    
    // Build link to insert
    link = document.createElement('a');
    link.href = href;
    link.innerText = selectionText;
    
    // Build link for after selection
    afterLink = document.createElement('a');
    afterLink.href = afterLinkHref || currentLink;
    afterLink.innerText = after;
    
    // Append the links in order
    if (beforeLink.innerText.length > 0) {
      parNode.appendChild(beforeLink);
    }
    parNode.appendChild(link);
    if (afterLink.innerText.length > 0) {
      parNode.appendChild(afterLink);
    }
    
    // remove old linking
    if (par === range.commonAncestorContainer) {
      par.removeChild(par.childNodes[0]);
      return par.removeChild(par.childNodes[1]);
    } else {
      return parNode.removeChild(par);
    }
  });
  return api.util.extend(api, {
    surroundSelectionWithLink: surroundSelectionWithLink
  });
});

describe("add_source", function() {
  var content, fixture, makeSelection;
  fixture = $('<div id="fixture"></div>');
  content = null;
  
  beforeEach(function() {
    return $('body').append(fixture);
  });
  
  afterEach(function() {
    $('body #fixture').empty();
    return $('body #fixture').remove();
  });
  
  makeSelection = (function(select) {
    var endEl, html, range, startEl;
    html = content.html();
    html = html.replace(select, '<span id="start"></span>' + select + '<span id="end"></span>');
    content.html(html);
    range = document.createRange();
    startEl = $('#start', content)[0];
    range.setStartBefore(startEl);
    endEl = $('#end', content)[0];
    range.setEndBefore(endEl);
    startEl.remove();
    endEl.remove();
    sel = rangy.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
  });
  
  it("inserts a link inside a an exsisting link", function() {
    content = $('<div contenteditbale><a href="http://www.example.com/">Linked New Text</a></div>');
    fixture.append(content);
    makeSelection('New');
    
    rangy.surroundSelectionWithLink('http://www.example.com/new');
    
    return expect(fixture.find('[contenteditbale]').html()).toEqual('<a href="http://www.example.com/">Linked </a><a href="http://www.example.com/new">New</a><a href="http://www.example.com/"> Text</a>');
  });
  it("replaces link with new link", function() {
    content = $('<div contenteditbale><a href="http://www.example.com/">Linked New Text</a></div>');
    fixture.append(content);
    makeSelection('Linked New Text');
    
    rangy.surroundSelectionWithLink('http://www.example.com/replaced');

    return expect(fixture.find('[contenteditbale]').html()).toEqual('<a href="http://www.example.com/replaced">Linked New Text</a>');
  });
  return it("inserts link between two links", function() {
    content = $('<div contenteditbale><a href="http://www.example.com/">Linked <span id="start"></span>New</a> <a href="http://www.example.com/next">The<span id="end"></span> Text</a></div>');
    fixture.append(content);
    makeSelection("New The");
    
    rangy.surroundSelectionWithLink('http://www.example.com/added');

    return expect(fixture.find('[contenteditbale]').html()).toEqual(' <a href="http://www.example.com/">Linked </a><a href="http://www.example.com/added">New The</a><a href="http://www.example.com/next"> Text</a>');
  });
});
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Selection Range test</title>

  <script src="http://pivotal.github.com/jasmine/lib/jasmine-1.3.1/jasmine.js"></script>
  <script src="http://pivotal.github.com/jasmine/lib/jasmine-1.3.1/jasmine-html.js"></script>
  <link href="http://pivotal.github.com/jasmine/lib/jasmine-1.3.1/jasmine.css"
      rel="stylesheet">

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script src="http://rangy.googlecode.com/svn/trunk/currentrelease/rangy-core.js"></script>

  <link rel="stylesheet" href="style.css">
  <script src="app.js"></script>
  <script src="appSpec.js"></script>
  <script src="jasmineBootstrap.js"></script> <!-- bootstraps Jasmine -->
</head>
<body>
  <div id="HTMLReporter" class="jasmine_reporter"></div>
</body>
</html>
(function() {
  var jasmineEnv = jasmine.getEnv();
  jasmineEnv.updateInterval = 250;

  /**
   Create the `HTMLReporter`, which Jasmine calls to provide results of each spec and each suite. The Reporter is responsible for presenting results to the user.
   */
  var htmlReporter = new jasmine.HtmlReporter();
  jasmineEnv.addReporter(htmlReporter);

  /**
   Delegate filtering of specs to the reporter. Allows for clicking on single suites or specs in the results to only run a subset of the suite.
   */
  jasmineEnv.specFilter = function(spec) {
    return htmlReporter.specFilter(spec);
  };

  /**
   Run all of the tests when the page finishes loading - and make sure to run any previous `onload` handler

   ### Test Results

   Scroll down to see the results of all of these specs.
   */
  var currentWindowOnload = window.onload;
  window.onload = function() {
    if (currentWindowOnload) {
      currentWindowOnload();
    }

    //document.querySelector('.version').innerHTML = jasmineEnv.versionString();
    execJasmine();
  };

  function execJasmine() {
    jasmineEnv.execute();
  }
})();
/* restore "body" styling that were changes by "jasmine.css"... */
body { background-color: white; padding: 0; margin: 8px; }
/* ... but remain the "jasmine.css" styling for the Jasmine reporting */
.jasmine_reporter { background-color: #eeeeee; padding: 0; margin: 0; }