Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improved plugin handling - plugins are now ensured a clean text-only …

…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...
commit 3c10dc03462c43932fe962826246daee13470f28 1 parent a4d3eaf
@maranomynet authored
View
2  1.0/jquery.linkify-1.0-min.js
@@ -1,3 +1,3 @@
// encoding: utf-8
// $.fn.linkify 1.0 - MIT/GPL Licensed - More info: http://github.com/maranomynet/linkify/
-(function(c){var f=/(^|"|&lt;|\s)(www\..+?\..+?)(\s|&gt;|"|$)/g,g=/(^|"|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(\s|&gt;|"|$)/g;c.fn.linkify=function(){return this.each(function(){var d=this.childNodes,e=d.length;while(e--){var a=d[e];if(a.nodeType==3){var b=a.nodeValue;if(/\S/.test(b)){b=b.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(f,'$1<a href="<``>://$2">$2</a>$3').replace(g,'$1<a href="$2">$2</a>$5').replace(/"<``>/g,'"http');c(a).after(b).remove()}}else if(a.nodeType==1&&!/^(a|button|textarea)$/i.test(a.tagName)){arguments.callee.call(a)}}})}})(jQuery);
+(function(b){var v=/(^|["'(]|&lt;|\s)(www\..+?\..+?)(([:?]|\.+)?(\s|$)|&gt;|[)"',])/g,w=/(^|["'(]|&lt;|\s)(((https?|ftp):\/\/|mailto:).+?)(([:?]|\.+)?(\s|$)|&gt;|[)"',])/g,x=function(h){return h.replace(v,'$1<a href="<``>://$2">$2</a>$3').replace(w,'$1<a href="$2">$2</a>$5').replace(/"<``>/g,'"http')},q=b.fn.linkify=function(e){e=e||{};if(typeof e=='string'){e={use:e}}var c=e.use,k=q.plugins||{},l=[x],f;if(c){c=b.isArray(c)?c:b.trim(c).split(/ *, */);var m,i;for(var n=0,y=c.length;n<y;n++){i=c[n];m=k[i];if(m){l.push(m)}}}else{for(var i in k){l.push(k[i])}}return this.each(function(){var h=this.childNodes,r=h.length;while(r--){var d=h[r];if(d.nodeType==3){var a=d.nodeValue;if(a.length>1&&/\S/.test(a)){var o,p;f=f||b('<div/>')[0];f.innerHTML='';f.appendChild(d.cloneNode(false));var s=f.childNodes;for(var t=0,g;(g=l[t]);t++){var u=s.length,j;while(u--){j=s[u];if(j.nodeType==3){a=j.nodeValue;if(a.length>1&&/\S/.test(a)){p=a;a=a.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');a=b.isFunction(g)?g(a):a.replace(g.re,g.tmpl);o=o||p!=a;p!=a&&b(j).after(a).remove()}}}}a=f.innerHTML;o&&b(d).after(a).remove()}}else if(d.nodeType==1&&!/^(a|button|textarea)$/i.test(d.tagName)){arguments.callee.call(d)}}})};q.plugins={}})(jQuery);
View
10 1.0/jquery.linkify-1.0-test.html
@@ -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 &nbsp; ==> '+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) {
@@ -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>
@@ -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>
View
86 1.0/jquery.linkify-1.0.js
@@ -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 || {};
@@ -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 () {
@@ -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) )
Please sign in to comment.
Something went wrong with that request. Please try again.