Skip to content

Commit

Permalink
Improved plugin handling - plugins are now ensured a clean text-only …
Browse files Browse the repository at this point in the history
…HTML input.

NOTE: The live DOM is only updated once per *changed* textNode (to minimize reflows)
However, even more optimizations are possible by...
a) avoiding the use of `tmpCont` when no plugins are present, and
b) by collecting a list of changed nodes and their replacement HTML strings and updating the live DOM afterwards in one fell loop.
  • Loading branch information
maranomynet committed Jun 11, 2010
1 parent a4d3eaf commit 3c10dc0
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 29 deletions.
2 changes: 1 addition & 1 deletion 1.0/jquery.linkify-1.0-min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions 1.0/jquery.linkify-1.0-test.html
Expand Up @@ -37,8 +37,10 @@

isOk = !pTitle ?
a.length === 0:
a.length === 1 && (a.attr('href')||'') === pTitle && a.text() === (pLinktext||pTitle);
ok( isOk, p.data('orgHtml')+'\n   ==> '+this.innerHTML );
a.length === pTitle.split(',').length
&& $.map(a, function(a){ return $(a).attr('href')||''; }).join(',') === pTitle
&& $.map(a, function(a){ return $(a).text(); }).join(',') === (pLinktext||pTitle);
ok( isOk, p.data('orgHtml')+'\n ==> '+this.innerHTML );
},

prepTest = function (selector) {
Expand Down Expand Up @@ -131,6 +133,7 @@ <h2 id="qunit-userAgent"></h2>
<p title="http://www.foo.is">http://www.foo.is</p>
<p title="http://www.foo.is/">http://www.foo.is/ asdf </p>
<p title="http://www.foo.is:8080">http://www.foo.is:8080. asd</p>
<p title="http://www.foo.is:8080,http://www.foo.is/#foo">http://www.foo.is:8080. asd (http://www.foo.is/#foo)...</p>
<p title="http://www.foo.is:8080/path/?foo=1#arg">http://www.foo.is:8080/path/?foo=1#arg #asdasdf</p>
<p title="http://www.foo.is/#foo">http://www.foo.is/#foo ?asddf</p>
<p title="http://www.foo.is/">should I visit http://www.foo.is/? Sure</p>
Expand All @@ -157,6 +160,9 @@ <h2 id="qunit-userAgent"></h2>

<div id="wplugin">
<p title="http://www.foo.is">http://www.foo.is</p>
<p title="http://www.foo.is,http://www.twitter.com/username" data-linktext="http://www.foo.is,@username">blah http://www.foo.is @username blah</p>
<p title="http://www.foo.is/,http://www.twitter.com/search?q=%23hashtag" data-linktext="http://www.foo.is/,#hashtag">blah &lt;http://www.foo.is/&gt; blah #hashtag blah</p>
<p title="http://www.foo.is/,http://www.twitter.com/search?q=%23hashtag" data-linktext="http://www.foo.is/,#hashtag">blah (http://www.foo.is/) (#hashtag) blah</p>
<p title="http://www.twitter.com/username" data-linktext="@username">@username</p>
<p title="http://www.twitter.com/username" data-linktext="@username">blah @username blah</p>
<p title="http://www.twitter.com/username" data-linktext="@username">(@username)</p>
Expand Down
86 changes: 60 additions & 26 deletions 1.0/jquery.linkify-1.0.js
Expand Up @@ -55,6 +55,13 @@

var noProtocolUrl = /(^|["'(]|&lt;|\s)(www\..+?\..+?)(([:?]|\.+)?(\s|$)|&gt;|[)"',])/g,
httpOrMailtoUrl = /(^|["'(]|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(([:?]|\.+)?(\s|$)|&gt;|[)"',])/g,
linkifier = function ( html ) {
return html
.replace( noProtocolUrl, '$1<a href="<``>://$2">$2</a>$3' ) // NOTE: we escape `"http` as `"<``>` to make sure `httpOrMailtoUrl` below doesn't find it as a false-positive
.replace( httpOrMailtoUrl, '$1<a href="$2">$2</a>$5' )
.replace( /"<``>/g, '"http' ); // reinsert `"http`
},


linkify = $.fn.linkify = function ( cfg ) {
cfg = cfg || {};
Expand All @@ -63,17 +70,30 @@
cfg = { use:cfg };
}
var use = cfg.use,
plugins = linkify.plugins||{};
allPlugins = linkify.plugins || {},
plugins = [linkifier],
tmpCont;
if ( use )
{
use = $.isArray( use ) ? use : $.trim(use).split( / *, */ );
var pluginShortlist = {};
for ( var i=0, l=use.length, name; i<l; i++ )
var plugin,
name;
for ( var i=0, l=use.length; i<l; i++ )
{
name = use[i];
pluginShortlist[name] = plugins[name];
};
plugins = pluginShortlist;
plugin = allPlugins[name];
if ( plugin )
{
plugins.push( plugin );
}
}
}
else
{
for ( var name in allPlugins )
{
plugins.push( allPlugins[name] );
}
}

return this.each(function () {
Expand All @@ -85,29 +105,43 @@
if ( n.nodeType == 3 )
{
var html = n.nodeValue;
if ( /\S/.test(html) )
if ( html.length>1 && /\S/.test(html) )
{
html = html
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' );
var preHtml = html;
html = html
.replace( noProtocolUrl, '$1<a href="<``>://$2">$2</a>$3' ) // NOTE: we escape `"http` as `"<``>` to make sure `httpOrMailtoUrl` below doesn't find it as a false-positive
.replace( httpOrMailtoUrl, '$1<a href="$2">$2</a>$5' )
.replace( /"<``>/g, '"http' ); // reinsert `"http`

for ( var name in plugins )
{
var plugin = plugins[name] || {};
html = $.isFunction( plugin ) ?
plugin( html ):
html.replace( plugin.re, plugin.tmpl );
}
if (html != preHtml)
var htmlChanged,
preHtml;
tmpCont = tmpCont || $('<div/>')[0];
tmpCont.innerHTML = '';
tmpCont.appendChild( n.cloneNode(false) );
var tmpContNodes = tmpCont.childNodes;

for (var j=0, plugin; (plugin = plugins[j]); j++)
{
$(n).after(html).remove();
var k = tmpContNodes.length,
tmpNode;
while ( k-- )
{
tmpNode = tmpContNodes[k];
if ( tmpNode.nodeType == 3 )
{
html = tmpNode.nodeValue;
if ( html.length>1 && /\S/.test(html) )
{
preHtml = html;
html = html
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' );
html = $.isFunction( plugin ) ?
plugin( html ):
html.replace( plugin.re, plugin.tmpl );
htmlChanged = htmlChanged || preHtml!=html;
preHtml!=html && $(tmpNode).after(html).remove();
}
}
}
}
html = tmpCont.innerHTML;
htmlChanged && $(n).after(html).remove();
}
}
else if ( n.nodeType == 1 && !/^(a|button|textarea)$/i.test(n.tagName) )
Expand Down

0 comments on commit 3c10dc0

Please sign in to comment.