diff --git a/absolutify.js b/absolutify.js index 1f4bfd1..6608590 100644 --- a/absolutify.js +++ b/absolutify.js @@ -9,9 +9,11 @@ * @return {String} replaced html source */ -function replace(str, url) { - if (typeof url === 'function') return replace.iterate(str, url) - return str.replace(replace.rx, '$1' + url + '/$4') +function absolutify(str, url) { + if (typeof url === 'function') return absolutify.iterate(str, url) + return str + .replace(absolutify.rx, '$1' + url + '/$6') // Inject the URL into the attribute + .replace(new RegExp(url + '//', 'g'), url + '/') // Fix `attr="/"` edgecase causing a `//` issue } /*! @@ -19,15 +21,15 @@ function replace(str, url) { * ensure that the leading `/` of the url is not captured * * HTML attribute list from: http://stackoverflow.com/questions/2725156/complete-list-of-html-tag-attributes-which-have-a-url-value - */ - -replace.rx = /((href|src|codebase|cite|background|cite|action|profile|formaction|icon|manifest|archive)=["'])(([.]+\/)|(?:\/)|(?=#))(?!\/)/g -/*! - * Match the same as above, but capture the full URL for iteration + 1. find any possible element containing a url: `prop="` + 2. ignore leading protocols: `prop="http:` + 3. ignore protocol skips `prop="//` + 4. find and omit leading slash to normalize: `prop="/url` + 5. */ -replace.captureRx = /((href|src|codebase|cite|background|cite|action|profile|formaction|icon|manifest|archive)=["'])((([.]+\/)|(?:\/)|(?:#))(?!\/)[a-zA-Z0-9._-]+)/g +absolutify.rx = /((href|src|codebase|cite|background|action|profile|formaction|icon|manifest|archive)=["'])(?!(http|https|ftp|file|filesystem|gopher|ws|wss|about|blob|data|mailto):|(\/\/))((?:\/)?([^'"]+))/g // jshint ignore:line /** * URL replacement using function iteration, this is handled slightly @@ -39,9 +41,15 @@ replace.captureRx = /((href|src|codebase|cite|background|cite|action|profile|for * @return {String} replaced html source */ -replace.iterate = function(str, iterator) { - return str.replace(replace.captureRx, function(full, prefix, prop, url) { - return prefix + iterator(url, prop) +absolutify.iterate = function(str, iterator) { + return str.replace(absolutify.rx, function() { + var url = arguments[6] + + return arguments[1] + iterator( + url === '/' ? '' : url // URL without leading `/` (check for `attr="/"` edgecase) + , arguments[2] // HTML attribute + , arguments[5] // Contains leading `/` if found + ) }) } @@ -49,7 +57,7 @@ replace.iterate = function(str, iterator) { * Exports */ -if (typeof exports !== 'undefined') module.exports = replace -else this.absolutify = replace +if (typeof exports !== 'undefined') module.exports = absolutify +else this.absolutify = absolutify }.call(this)); diff --git a/test.js b/test.js index 7f377d6..cccce14 100644 --- a/test.js +++ b/test.js @@ -7,120 +7,297 @@ describe('absolutify', function() { // Non-changing string, should not get replaced var ok = '' - + '' - + '' + + '' + + '' + '' + '' + + '' + + '' - it('string replace', function() { - assert.strictEqual( - absolutify( - 'Heyo' + ok - , 'http://www.example.com' + describe('string replace', function() { + it('ignores valid values', function() { + assert.strictEqual( + absolutify(ok, 'http://www.example.com') + , ok ) - , 'Heyo' + ok - ) + }) - assert.strictEqual( - absolutify( - 'Heyo' + ok - , 'http://www.example.com' + it('/url', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , 'http://www.example.com' + ) + , 'Heyo' ) - , 'Heyo' + ok - ) + }) - assert.strictEqual( - absolutify( - 'Heyo' + ok - , 'http://www.example.com' + it('../url', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , 'http://www.example.com' + ) + , 'Heyo' ) - , 'Heyo' + ok - ) - }) + }) - it('string replace single quote', function() { - assert.strictEqual( - absolutify( - "Heyo" + ok - , 'http://www.example.com' + it('/', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , 'http://www.example.com' + ) + , 'Heyo' ) - , "Heyo" + ok - ) - }) + }) - it('string multi-replace', function() { - assert.strictEqual( - absolutify( - 'Heyo
' + ok - , 'http://www.example.com' + it(':port/url', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com:80' + ) + , 'Section' ) - , 'Heyo' + ok - ) - }) + }) - it('string replace anchor', function() { - assert.strictEqual( - absolutify( - 'Section' + ok - , 'http://www.example.com' + it('#anchor', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com' + ) + , 'Section' ) - , 'Section' + ok - ) - }) + }) - it('function replace', function() { - assert.strictEqual( - absolutify( - 'Heyo' + ok - , function(url, attr) { - return 'http://www.example.com' + url - } - ) - , 'Heyo' + ok - ) - - assert.strictEqual( - absolutify( - 'Heyo' + ok - , function(url, attr) { - return 'http://www.example.com/public/' + url - } - ) - , 'Heyo' + ok - ) - - assert.strictEqual( - absolutify( - 'Heyo' + ok - , function(url, attr) { - return 'http://www.example.com/' + url - } - ) - , 'Heyo' + ok - ) - }) + it('url.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com' + ) + , 'Section' + ) + }) - it('function replace anchor', function() { - assert.strictEqual( - absolutify( - 'Section' + ok - , function(url, attr) { - return 'http://www.example.com' + url - } + it('url/dir/file.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com' + ) + , 'Section' + ) + }) + + it('mailto.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com' + ) + , 'Section' + ) + }) + + it('file.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , 'http://www.example.com' + ) + , 'Section' + ) + }) + + it('../url (quote test)', function() { + assert.strictEqual( + absolutify( + "Heyo" + , 'http://www.example.com' + ) + , "Heyo" ) - , 'Section' + ok - ) + }) + + it('multi-replace', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , 'http://www.example.com' + ) + , 'Heyo' + ) + }) + + it('/url?querystring[]=val', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , 'http://www.example.com' + ) + , 'Heyo' + ) + }) }) - it('function multi-replace', function() { - assert.strictEqual( - absolutify( - 'Heyo' + ok - , function(url, attr) { + describe('function replace', function() { + it('ignores valid urls', function() { + assert.strictEqual( + absolutify(ok, function(url, attr) { return 'http://www.example.com' + url - } + }) + , ok + ) + }) + + it('/url', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , function(url, attr, full) { + assert.strictEqual(url, 'relative') + assert.strictEqual(attr, 'profile') + assert.strictEqual(full, '/relative') + return 'http://www.example.com/' + url + } + ) + , 'Heyo' + ) + }) + + it('/url?querystring[]=val', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , function(url, attr, full) { + assert.strictEqual(url, 'relative?foo[]=bar&cat=meow') + assert.strictEqual(attr, 'archive') + assert.strictEqual(full, '/relative?foo[]=bar&cat=meow') + return 'http://www.example.com/' + url + } + ) + , 'Heyo' + ) + }) + + it('/', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , function(url, attr, full) { + assert.strictEqual(url, '') + assert.strictEqual(attr, 'profile') + assert.strictEqual(full, '/') + return 'http://www.example.com/' + url + } + ) + , 'Heyo' + ) + }) + + it('../url', function() { + assert.strictEqual( + absolutify( + '' + , function(url, attr) { + assert.strictEqual(url, '../two') + assert.strictEqual(attr, 'src') + return 'http://www.example.com/public/' + url + } + ) + , '' + ) + }) + + it('./url', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , function(url, attr, full) { + assert.strictEqual(url, './three') + assert.strictEqual(full, './three') + assert.strictEqual(attr, 'href') + return 'http://www.example.com/' + url + } + ) + , 'Heyo' + ) + }) + + it('#anchor', function() { + assert.strictEqual( + absolutify( + 'Section' + , function(url, attr, full) { + assert.strictEqual(url, '#section') + assert.strictEqual(full, '#section') + assert.strictEqual(attr, 'href') + return 'http://www.example.com' + url + } + ) + , 'Section' + ) + }) + + it('url.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , function(url, attr, full) { + assert.strictEqual(url, 'index.php') + assert.strictEqual(full, 'index.php') + assert.strictEqual(attr, 'href') + return 'http://www.example.com/' + url + } + ) + , 'Section' + ) + }) + + it('/url.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , function(url, attr, full) { + assert.strictEqual(url, 'index.php') + assert.strictEqual(full, '/index.php') + assert.strictEqual(attr, 'href') + return 'http://www.example.com/' + url + } + ) + , 'Section' + ) + }) + + it('url/dir/file.ext', function() { + assert.strictEqual( + absolutify( + 'Section' + , function(url, attr) { + assert.strictEqual(url, 'nested/dir/index.php') + assert.strictEqual(attr, 'href') + return 'http://www.example.com/' + url + } + ) + , 'Section' + ) + }) + + it('multi-replace', function() { + assert.strictEqual( + absolutify( + 'Heyo' + , function(url, attr) { + // assert.strictEqual(url, '/relative') + // assert.strictEqual(attr, 'href') + return 'http://www.example.com/' + url + } + ) + , 'Heyo' ) - , 'Heyo' + ok - ) + }) }) })