autolinker
Advanced tools
Comparing version 0.15.3 to 0.16.0
/*! | ||
* Autolinker.js | ||
* 0.15.3 | ||
* 0.16.0 | ||
* | ||
@@ -10,2 +10,2 @@ * Copyright(c) 2015 Gregory Jacobs <greg@greg-jacobs.com> | ||
*/ | ||
!function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.Autolinker=b()}):"object"==typeof exports?module.exports=b():a.Autolinker=b()}(this,function(){var a=function(b){a.Util.assign(this,b)};return a.prototype={constructor:a,urls:!0,email:!0,twitter:!0,newWindow:!0,stripPrefix:!0,truncate:void 0,className:"",htmlParser:void 0,matchParser:void 0,tagBuilder:void 0,link:function(a){for(var b=this.getHtmlParser(),c=b.parse(a),d=0,e=[],f=0,g=c.length;g>f;f++){var h=c[f],i=h.getType(),j=h.getText();if("element"===i)"a"===h.getTagName()&&(h.isClosing()?d=Math.max(d-1,0):d++),e.push(j);else if("entity"===i)e.push(j);else if(0===d){var k=this.linkifyStr(j);e.push(k)}else e.push(j)}return e.join("")},linkifyStr:function(a){return this.getMatchParser().replace(a,this.createMatchReturnVal,this)},createMatchReturnVal:function(b){var c;if(this.replaceFn&&(c=this.replaceFn.call(this,this,b)),"string"==typeof c)return c;if(c===!1)return b.getMatchedText();if(c instanceof a.HtmlTag)return c.toString();var d=this.getTagBuilder(),e=d.build(b);return e.toString()},getHtmlParser:function(){var b=this.htmlParser;return b||(b=this.htmlParser=new a.htmlParser.HtmlParser),b},getMatchParser:function(){var b=this.matchParser;return b||(b=this.matchParser=new a.matchParser.MatchParser({urls:this.urls,email:this.email,twitter:this.twitter,stripPrefix:this.stripPrefix})),b},getTagBuilder:function(){var b=this.tagBuilder;return b||(b=this.tagBuilder=new a.AnchorTagBuilder({newWindow:this.newWindow,truncate:this.truncate,className:this.className})),b}},a.link=function(b,c){var d=new a(c);return d.link(b)},a.match={},a.htmlParser={},a.matchParser={},a.Util={abstractMethod:function(){throw"abstract"},assign:function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a},extend:function(b,c){var d=b.prototype,e=function(){};e.prototype=d;var f;f=c.hasOwnProperty("constructor")?c.constructor:function(){d.constructor.apply(this,arguments)};var g=f.prototype=new e;return g.constructor=f,g.superclass=d,delete c.constructor,a.Util.assign(g,c),f},ellipsis:function(a,b,c){return a.length>b&&(c=null==c?"..":c,a=a.substring(0,b-c.length)+c),a},indexOf:function(a,b){if(Array.prototype.indexOf)return a.indexOf(b);for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},splitAndCapture:function(a,b){if(!b.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var c,d=[],e=0;c=b.exec(a);)d.push(a.substring(e,c.index)),d.push(c[0]),e=c.index+c[0].length;return d.push(a.substring(e)),d}},a.HtmlTag=a.Util.extend(Object,{whitespaceRegex:/\s+/,constructor:function(b){a.Util.assign(this,b),this.innerHtml=this.innerHtml||this.innerHTML},setTagName:function(a){return this.tagName=a,this},getTagName:function(){return this.tagName||""},setAttr:function(a,b){var c=this.getAttrs();return c[a]=b,this},getAttr:function(a){return this.getAttrs()[a]},setAttrs:function(b){var c=this.getAttrs();return a.Util.assign(c,b),this},getAttrs:function(){return this.attrs||(this.attrs={})},setClass:function(a){return this.setAttr("class",a)},addClass:function(b){for(var c,d=this.getClass(),e=this.whitespaceRegex,f=a.Util.indexOf,g=d?d.split(e):[],h=b.split(e);c=h.shift();)-1===f(g,c)&&g.push(c);return this.getAttrs()["class"]=g.join(" "),this},removeClass:function(b){for(var c,d=this.getClass(),e=this.whitespaceRegex,f=a.Util.indexOf,g=d?d.split(e):[],h=b.split(e);g.length&&(c=h.shift());){var i=f(g,c);-1!==i&&g.splice(i,1)}return this.getAttrs()["class"]=g.join(" "),this},getClass:function(){return this.getAttrs()["class"]||""},hasClass:function(a){return-1!==(" "+this.getClass()+" ").indexOf(" "+a+" ")},setInnerHtml:function(a){return this.innerHtml=a,this},getInnerHtml:function(){return this.innerHtml||""},toString:function(){var a=this.getTagName(),b=this.buildAttrsStr();return b=b?" "+b:"",["<",a,b,">",this.getInnerHtml(),"</",a,">"].join("")},buildAttrsStr:function(){if(!this.attrs)return"";var a=this.getAttrs(),b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+'="'+a[c]+'"');return b.join(" ")}}),a.AnchorTagBuilder=a.Util.extend(Object,{constructor:function(b){a.Util.assign(this,b)},build:function(b){var c=new a.HtmlTag({tagName:"a",attrs:this.createAttrs(b.getType(),b.getAnchorHref()),innerHtml:this.processAnchorText(b.getAnchorText())});return c},createAttrs:function(a,b){var c={href:b},d=this.createCssClass(a);return d&&(c["class"]=d),this.newWindow&&(c.target="_blank"),c},createCssClass:function(a){var b=this.className;return b?b+" "+b+"-"+a:""},processAnchorText:function(a){return a=this.doTruncate(a)},doTruncate:function(b){return a.Util.ellipsis(b,this.truncate||Number.POSITIVE_INFINITY)}}),a.htmlParser.HtmlParser=a.Util.extend(Object,{htmlRegex:function(){var a=/[0-9a-zA-Z][0-9a-zA-Z:]*/,b=/[^\s\0"'>\/=\x01-\x1F\x7F]+/,c=/(?:"[^"]*?"|'[^']*?'|[^'"=<>`\s]+)/,d=b.source+"(?:\\s*=\\s*"+c.source+")?";return new RegExp(["(?:","<(!DOCTYPE)","(?:","\\s+","(?:",d,"|",c.source+")",")*",">",")","|","(?:","<(/)?","("+a.source+")","(?:","\\s+",d,")*","\\s*/?",">",")"].join(""),"gi")}(),htmlCharacterEntitiesRegex:/( | |<|<|>|>|"|"|')/gi,parse:function(a){for(var b,c,d=this.htmlRegex,e=0,f=[];null!==(b=d.exec(a));){var g=b[0],h=b[1]||b[3],i=!!b[2],j=a.substring(e,b.index);j&&(c=this.parseTextAndEntityNodes(j),f.push.apply(f,c)),f.push(this.createElementNode(g,h,i)),e=b.index+g.length}if(e<a.length){var k=a.substring(e);k&&(c=this.parseTextAndEntityNodes(k),f.push.apply(f,c))}return f},parseTextAndEntityNodes:function(b){for(var c=[],d=a.Util.splitAndCapture(b,this.htmlCharacterEntitiesRegex),e=0,f=d.length;f>e;e+=2){var g=d[e],h=d[e+1];g&&c.push(this.createTextNode(g)),h&&c.push(this.createEntityNode(h))}return c},createElementNode:function(b,c,d){return new a.htmlParser.ElementNode({text:b,tagName:c.toLowerCase(),closing:d})},createEntityNode:function(b){return new a.htmlParser.EntityNode({text:b})},createTextNode:function(b){return new a.htmlParser.TextNode({text:b})}}),a.htmlParser.HtmlNode=a.Util.extend(Object,{text:"",constructor:function(b){a.Util.assign(this,b)},getType:a.Util.abstractMethod,getText:function(){return this.text}}),a.htmlParser.ElementNode=a.Util.extend(a.htmlParser.HtmlNode,{tagName:"",closing:!1,getType:function(){return"element"},getTagName:function(){return this.tagName},isClosing:function(){return this.closing}}),a.htmlParser.EntityNode=a.Util.extend(a.htmlParser.HtmlNode,{getType:function(){return"entity"}}),a.htmlParser.TextNode=a.Util.extend(a.htmlParser.HtmlNode,{getType:function(){return"text"}}),a.matchParser.MatchParser=a.Util.extend(Object,{urls:!0,email:!0,twitter:!0,stripPrefix:!0,matcherRegex:function(){var a=/(^|[^\w])@(\w{1,15})/,b=/(?:[\-;:&=\+\$,\w\.]+@)/,c=/(?:[A-Za-z][-.+A-Za-z0-9]+:(?![A-Za-z][-.+A-Za-z0-9]+:\/\/)(?!\d+\/?)(?:\/\/)?)/,d=/(?:www\.)/,e=/[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/,f=/\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/,g=/[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]?!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]]/;return new RegExp(["(",a.source,")","|","(",b.source,e.source,f.source,")","|","(","(?:","(",c.source,e.source,")","|","(?:","(.?//)?",d.source,e.source,")","|","(?:","(.?//)?",e.source,f.source,")",")","(?:"+g.source+")?",")"].join(""),"gi")}(),charBeforeProtocolRelMatchRegex:/^(.)?\/\//,constructor:function(b){a.Util.assign(this,b),this.matchValidator=new a.MatchValidator},replace:function(a,b,c){var d=this;return a.replace(this.matcherRegex,function(a,e,f,g,h,i,j,k,l){var m=d.processCandidateMatch(a,e,f,g,h,i,j,k,l);if(m){var n=b.call(c,m.match);return m.prefixStr+n+m.suffixStr}return a})},processCandidateMatch:function(b,c,d,e,f,g,h,i,j){var k,l=i||j,m="",n="";if(c&&!this.twitter||f&&!this.email||g&&!this.urls||!this.matchValidator.isValidMatch(g,h,l))return null;if(this.matchHasUnbalancedClosingParen(b)&&(b=b.substr(0,b.length-1),n=")"),f)k=new a.match.Email({matchedText:b,email:f});else if(c)d&&(m=d,b=b.slice(1)),k=new a.match.Twitter({matchedText:b,twitterHandle:e});else{if(l){var o=l.match(this.charBeforeProtocolRelMatchRegex)[1]||"";o&&(m=o,b=b.slice(1))}k=new a.match.Url({matchedText:b,url:b,protocolUrlMatch:!!h,protocolRelativeMatch:!!l,stripPrefix:this.stripPrefix})}return{prefixStr:m,suffixStr:n,match:k}},matchHasUnbalancedClosingParen:function(a){var b=a.charAt(a.length-1);if(")"===b){var c=a.match(/\(/g),d=a.match(/\)/g),e=c&&c.length||0,f=d&&d.length||0;if(f>e)return!0}return!1}}),a.MatchValidator=a.Util.extend(Object,{invalidProtocolRelMatchRegex:/^[\w]\/\//,hasFullProtocolRegex:/^[A-Za-z][-.+A-Za-z0-9]+:\/\//,uriSchemeRegex:/^[A-Za-z][-.+A-Za-z0-9]+:/,hasWordCharAfterProtocolRegex:/:[^\s]*?[A-Za-z]/,isValidMatch:function(a,b,c){return b&&!this.isValidUriScheme(b)||this.urlMatchDoesNotHaveProtocolOrDot(a,b)||this.urlMatchDoesNotHaveAtLeastOneWordChar(a,b)||this.isInvalidProtocolRelativeMatch(c)?!1:!0},isValidUriScheme:function(a){var b=a.match(this.uriSchemeRegex)[0].toLowerCase();return"javascript:"!==b&&"vbscript:"!==b},urlMatchDoesNotHaveProtocolOrDot:function(a,b){return!(!a||b&&this.hasFullProtocolRegex.test(b)||-1!==a.indexOf("."))},urlMatchDoesNotHaveAtLeastOneWordChar:function(a,b){return a&&b?!this.hasWordCharAfterProtocolRegex.test(a):!1},isInvalidProtocolRelativeMatch:function(a){return!!a&&this.invalidProtocolRelMatchRegex.test(a)}}),a.match.Match=a.Util.extend(Object,{constructor:function(b){a.Util.assign(this,b)},getType:a.Util.abstractMethod,getMatchedText:function(){return this.matchedText},getAnchorHref:a.Util.abstractMethod,getAnchorText:a.Util.abstractMethod}),a.match.Email=a.Util.extend(a.match.Match,{getType:function(){return"email"},getEmail:function(){return this.email},getAnchorHref:function(){return"mailto:"+this.email},getAnchorText:function(){return this.email}}),a.match.Twitter=a.Util.extend(a.match.Match,{getType:function(){return"twitter"},getTwitterHandle:function(){return this.twitterHandle},getAnchorHref:function(){return"https://twitter.com/"+this.twitterHandle},getAnchorText:function(){return"@"+this.twitterHandle}}),a.match.Url=a.Util.extend(a.match.Match,{urlPrefixRegex:/^(https?:\/\/)?(www\.)?/i,protocolRelativeRegex:/^\/\//,protocolPrepended:!1,getType:function(){return"url"},getUrl:function(){var a=this.url;return this.protocolRelativeMatch||this.protocolUrlMatch||this.protocolPrepended||(a=this.url="http://"+a,this.protocolPrepended=!0),a},getAnchorHref:function(){var a=this.getUrl();return a.replace(/&/g,"&")},getAnchorText:function(){var a=this.getUrl();return this.protocolRelativeMatch&&(a=this.stripProtocolRelativePrefix(a)),this.stripPrefix&&(a=this.stripUrlPrefix(a)),a=this.removeTrailingSlash(a)},stripUrlPrefix:function(a){return a.replace(this.urlPrefixRegex,"")},stripProtocolRelativePrefix:function(a){return a.replace(this.protocolRelativeRegex,"")},removeTrailingSlash:function(a){return"/"===a.charAt(a.length-1)&&(a=a.slice(0,-1)),a}}),a}); | ||
!function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.Autolinker=b()}):"object"==typeof exports?module.exports=b():a.Autolinker=b()}(this,function(){var a=function(b){a.Util.assign(this,b)};return a.prototype={constructor:a,urls:!0,email:!0,twitter:!0,phone:!0,newWindow:!0,stripPrefix:!0,truncate:void 0,className:"",htmlParser:void 0,matchParser:void 0,tagBuilder:void 0,link:function(a){for(var b=this.getHtmlParser(),c=b.parse(a),d=0,e=[],f=0,g=c.length;g>f;f++){var h=c[f],i=h.getType(),j=h.getText();if("element"===i)"a"===h.getTagName()&&(h.isClosing()?d=Math.max(d-1,0):d++),e.push(j);else if("entity"===i)e.push(j);else if(0===d){var k=this.linkifyStr(j);e.push(k)}else e.push(j)}return e.join("")},linkifyStr:function(a){return this.getMatchParser().replace(a,this.createMatchReturnVal,this)},createMatchReturnVal:function(b){var c;if(this.replaceFn&&(c=this.replaceFn.call(this,this,b)),"string"==typeof c)return c;if(c===!1)return b.getMatchedText();if(c instanceof a.HtmlTag)return c.toAnchorString();var d=this.getTagBuilder(),e=d.build(b);return e.toAnchorString()},getHtmlParser:function(){var b=this.htmlParser;return b||(b=this.htmlParser=new a.htmlParser.HtmlParser),b},getMatchParser:function(){var b=this.matchParser;return b||(b=this.matchParser=new a.matchParser.MatchParser({urls:this.urls,email:this.email,twitter:this.twitter,phone:this.phone,stripPrefix:this.stripPrefix})),b},getTagBuilder:function(){var b=this.tagBuilder;return b||(b=this.tagBuilder=new a.AnchorTagBuilder({newWindow:this.newWindow,truncate:this.truncate,className:this.className})),b}},a.link=function(b,c){var d=new a(c);return d.link(b)},a.match={},a.htmlParser={},a.matchParser={},a.Util={abstractMethod:function(){throw"abstract"},assign:function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a},extend:function(b,c){var d=b.prototype,e=function(){};e.prototype=d;var f;f=c.hasOwnProperty("constructor")?c.constructor:function(){d.constructor.apply(this,arguments)};var g=f.prototype=new e;return g.constructor=f,g.superclass=d,delete c.constructor,a.Util.assign(g,c),f},ellipsis:function(a,b,c){return a.length>b&&(c=null==c?"..":c,a=a.substring(0,b-c.length)+c),a},indexOf:function(a,b){if(Array.prototype.indexOf)return a.indexOf(b);for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},splitAndCapture:function(a,b){if(!b.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var c,d=[],e=0;c=b.exec(a);)d.push(a.substring(e,c.index)),d.push(c[0]),e=c.index+c[0].length;return d.push(a.substring(e)),d}},a.HtmlTag=a.Util.extend(Object,{whitespaceRegex:/\s+/,constructor:function(b){a.Util.assign(this,b),this.innerHtml=this.innerHtml||this.innerHTML},setTagName:function(a){return this.tagName=a,this},getTagName:function(){return this.tagName||""},setAttr:function(a,b){var c=this.getAttrs();return c[a]=b,this},getAttr:function(a){return this.getAttrs()[a]},setAttrs:function(b){var c=this.getAttrs();return a.Util.assign(c,b),this},getAttrs:function(){return this.attrs||(this.attrs={})},setClass:function(a){return this.setAttr("class",a)},addClass:function(b){for(var c,d=this.getClass(),e=this.whitespaceRegex,f=a.Util.indexOf,g=d?d.split(e):[],h=b.split(e);c=h.shift();)-1===f(g,c)&&g.push(c);return this.getAttrs()["class"]=g.join(" "),this},removeClass:function(b){for(var c,d=this.getClass(),e=this.whitespaceRegex,f=a.Util.indexOf,g=d?d.split(e):[],h=b.split(e);g.length&&(c=h.shift());){var i=f(g,c);-1!==i&&g.splice(i,1)}return this.getAttrs()["class"]=g.join(" "),this},getClass:function(){return this.getAttrs()["class"]||""},hasClass:function(a){return-1!==(" "+this.getClass()+" ").indexOf(" "+a+" ")},setInnerHtml:function(a){return this.innerHtml=a,this},getInnerHtml:function(){return this.innerHtml||""},toAnchorString:function(){var a=this.getTagName(),b=this.buildAttrsStr();return b=b?" "+b:"",["<",a,b,">",this.getInnerHtml(),"</",a,">"].join("")},buildAttrsStr:function(){if(!this.attrs)return"";var a=this.getAttrs(),b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+'="'+a[c]+'"');return b.join(" ")}}),a.AnchorTagBuilder=a.Util.extend(Object,{constructor:function(b){a.Util.assign(this,b)},build:function(b){var c=new a.HtmlTag({tagName:"a",attrs:this.createAttrs(b.getType(),b.getAnchorHref()),innerHtml:this.processAnchorText(b.getAnchorText())});return c},createAttrs:function(a,b){var c={href:b},d=this.createCssClass(a);return d&&(c["class"]=d),this.newWindow&&(c.target="_blank"),c},createCssClass:function(a){var b=this.className;return b?b+" "+b+"-"+a:""},processAnchorText:function(a){return a=this.doTruncate(a)},doTruncate:function(b){return a.Util.ellipsis(b,this.truncate||Number.POSITIVE_INFINITY)}}),a.htmlParser.HtmlParser=a.Util.extend(Object,{htmlRegex:function(){var a=/[0-9a-zA-Z][0-9a-zA-Z:]*/,b=/[^\s\0"'>\/=\x01-\x1F\x7F]+/,c=/(?:"[^"]*?"|'[^']*?'|[^'"=<>`\s]+)/,d=b.source+"(?:\\s*=\\s*"+c.source+")?";return new RegExp(["(?:","<(!DOCTYPE)","(?:","\\s+","(?:",d,"|",c.source+")",")*",">",")","|","(?:","<(/)?","("+a.source+")","(?:","\\s+",d,")*","\\s*/?",">",")"].join(""),"gi")}(),htmlCharacterEntitiesRegex:/( | |<|<|>|>|"|"|')/gi,parse:function(a){for(var b,c,d=this.htmlRegex,e=0,f=[];null!==(b=d.exec(a));){var g=b[0],h=b[1]||b[3],i=!!b[2],j=a.substring(e,b.index);j&&(c=this.parseTextAndEntityNodes(j),f.push.apply(f,c)),f.push(this.createElementNode(g,h,i)),e=b.index+g.length}if(e<a.length){var k=a.substring(e);k&&(c=this.parseTextAndEntityNodes(k),f.push.apply(f,c))}return f},parseTextAndEntityNodes:function(b){for(var c=[],d=a.Util.splitAndCapture(b,this.htmlCharacterEntitiesRegex),e=0,f=d.length;f>e;e+=2){var g=d[e],h=d[e+1];g&&c.push(this.createTextNode(g)),h&&c.push(this.createEntityNode(h))}return c},createElementNode:function(b,c,d){return new a.htmlParser.ElementNode({text:b,tagName:c.toLowerCase(),closing:d})},createEntityNode:function(b){return new a.htmlParser.EntityNode({text:b})},createTextNode:function(b){return new a.htmlParser.TextNode({text:b})}}),a.htmlParser.HtmlNode=a.Util.extend(Object,{text:"",constructor:function(b){a.Util.assign(this,b)},getType:a.Util.abstractMethod,getText:function(){return this.text}}),a.htmlParser.ElementNode=a.Util.extend(a.htmlParser.HtmlNode,{tagName:"",closing:!1,getType:function(){return"element"},getTagName:function(){return this.tagName},isClosing:function(){return this.closing}}),a.htmlParser.EntityNode=a.Util.extend(a.htmlParser.HtmlNode,{getType:function(){return"entity"}}),a.htmlParser.TextNode=a.Util.extend(a.htmlParser.HtmlNode,{getType:function(){return"text"}}),a.matchParser.MatchParser=a.Util.extend(Object,{urls:!0,email:!0,twitter:!0,phone:!0,stripPrefix:!0,matcherRegex:function(){var a=/(^|[^\w])@(\w{1,15})/,b=/(?:[\-;:&=\+\$,\w\.]+@)/,c=/(?:\+?\d{1,3}[-\s.])?\(?\d{3}\)?[-\s.]?\d{3}[-\s.]\d{4}/,d=/(?:[A-Za-z][-.+A-Za-z0-9]+:(?![A-Za-z][-.+A-Za-z0-9]+:\/\/)(?!\d+\/?)(?:\/\/)?)/,e=/(?:www\.)/,f=/[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/,g=/\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/,h=/[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]?!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]]/;return new RegExp(["(",a.source,")","|","(",b.source,f.source,g.source,")","|","(","(?:","(",d.source,f.source,")","|","(?:","(.?//)?",e.source,f.source,")","|","(?:","(.?//)?",f.source,g.source,")",")","(?:"+h.source+")?",")","|","(",c.source,")"].join(""),"gi")}(),charBeforeProtocolRelMatchRegex:/^(.)?\/\//,constructor:function(b){a.Util.assign(this,b),this.matchValidator=new a.MatchValidator},replace:function(a,b,c){var d=this;return a.replace(this.matcherRegex,function(a,e,f,g,h,i,j,k,l,m){var n=d.processCandidateMatch(a,e,f,g,h,i,j,k,l,m);if(n){var o=b.call(c,n.match);return n.prefixStr+o+n.suffixStr}return a})},processCandidateMatch:function(b,c,d,e,f,g,h,i,j,k){var l,m=i||j,n="",o="";if(c&&!this.twitter||f&&!this.email||g&&!this.urls||k&&!this.phone||!this.matchValidator.isValidMatch(g,h,m))return null;if(this.matchHasUnbalancedClosingParen(b)&&(b=b.substr(0,b.length-1),o=")"),f)l=new a.match.Email({matchedText:b,email:f});else if(c)d&&(n=d,b=b.slice(1)),l=new a.match.Twitter({matchedText:b,twitterHandle:e});else if(k){var p=b.replace(/\D/g,"");l=new a.match.Phone({matchedText:b,number:p})}else{if(m){var q=m.match(this.charBeforeProtocolRelMatchRegex)[1]||"";q&&(n=q,b=b.slice(1))}l=new a.match.Url({matchedText:b,url:b,protocolUrlMatch:!!h,protocolRelativeMatch:!!m,stripPrefix:this.stripPrefix})}return{prefixStr:n,suffixStr:o,match:l}},matchHasUnbalancedClosingParen:function(a){var b=a.charAt(a.length-1);if(")"===b){var c=a.match(/\(/g),d=a.match(/\)/g),e=c&&c.length||0,f=d&&d.length||0;if(f>e)return!0}return!1}}),a.MatchValidator=a.Util.extend(Object,{invalidProtocolRelMatchRegex:/^[\w]\/\//,hasFullProtocolRegex:/^[A-Za-z][-.+A-Za-z0-9]+:\/\//,uriSchemeRegex:/^[A-Za-z][-.+A-Za-z0-9]+:/,hasWordCharAfterProtocolRegex:/:[^\s]*?[A-Za-z]/,isValidMatch:function(a,b,c){return b&&!this.isValidUriScheme(b)||this.urlMatchDoesNotHaveProtocolOrDot(a,b)||this.urlMatchDoesNotHaveAtLeastOneWordChar(a,b)||this.isInvalidProtocolRelativeMatch(c)?!1:!0},isValidUriScheme:function(a){var b=a.match(this.uriSchemeRegex)[0].toLowerCase();return"javascript:"!==b&&"vbscript:"!==b},urlMatchDoesNotHaveProtocolOrDot:function(a,b){return!(!a||b&&this.hasFullProtocolRegex.test(b)||-1!==a.indexOf("."))},urlMatchDoesNotHaveAtLeastOneWordChar:function(a,b){return a&&b?!this.hasWordCharAfterProtocolRegex.test(a):!1},isInvalidProtocolRelativeMatch:function(a){return!!a&&this.invalidProtocolRelMatchRegex.test(a)}}),a.match.Match=a.Util.extend(Object,{constructor:function(b){a.Util.assign(this,b)},getType:a.Util.abstractMethod,getMatchedText:function(){return this.matchedText},getAnchorHref:a.Util.abstractMethod,getAnchorText:a.Util.abstractMethod}),a.match.Email=a.Util.extend(a.match.Match,{getType:function(){return"email"},getEmail:function(){return this.email},getAnchorHref:function(){return"mailto:"+this.email},getAnchorText:function(){return this.email}}),a.match.Phone=a.Util.extend(a.match.Match,{getType:function(){return"phone"},getNumber:function(){return this.number},getAnchorHref:function(){return"tel:"+this.number},getAnchorText:function(){return this.matchedText}}),a.match.Twitter=a.Util.extend(a.match.Match,{getType:function(){return"twitter"},getTwitterHandle:function(){return this.twitterHandle},getAnchorHref:function(){return"https://twitter.com/"+this.twitterHandle},getAnchorText:function(){return"@"+this.twitterHandle}}),a.match.Url=a.Util.extend(a.match.Match,{urlPrefixRegex:/^(https?:\/\/)?(www\.)?/i,protocolRelativeRegex:/^\/\//,protocolPrepended:!1,getType:function(){return"url"},getUrl:function(){var a=this.url;return this.protocolRelativeMatch||this.protocolUrlMatch||this.protocolPrepended||(a=this.url="http://"+a,this.protocolPrepended=!0),a},getAnchorHref:function(){var a=this.getUrl();return a.replace(/&/g,"&")},getAnchorText:function(){var a=this.getUrl();return this.protocolRelativeMatch&&(a=this.stripProtocolRelativePrefix(a)),this.stripPrefix&&(a=this.stripUrlPrefix(a)),a=this.removeTrailingSlash(a)},stripUrlPrefix:function(a){return a.replace(this.urlPrefixRegex,"")},stripProtocolRelativePrefix:function(a){return a.replace(this.protocolRelativeRegex,"")},removeTrailingSlash:function(a){return"/"===a.charAt(a.length-1)&&(a=a.slice(0,-1)),a}}),a}); |
@@ -5,3 +5,3 @@ /*global require, module */ | ||
'use strict'; | ||
// Tasks | ||
@@ -13,4 +13,4 @@ grunt.registerTask( 'default', [ 'jshint', 'build', 'jasmine' ] ); | ||
grunt.registerTask( 'serve', [ 'connect:server:keepalive' ] ); | ||
// Project configuration | ||
@@ -21,6 +21,6 @@ var exec = require( 'child_process' ).exec, | ||
minDistPath = 'dist/Autolinker.min.js'; | ||
grunt.initConfig( { | ||
pkg: grunt.file.readJSON( 'package.json' ), | ||
connect: { | ||
@@ -35,3 +35,3 @@ server: { | ||
}, | ||
jshint: { | ||
@@ -42,3 +42,3 @@ files: { | ||
}, | ||
jasmine: { | ||
@@ -52,3 +52,3 @@ dist: { | ||
}, | ||
concat: { | ||
@@ -74,2 +74,3 @@ development: { | ||
'src/match/Email.js', | ||
'src/match/Phone.js', | ||
'src/match/Twitter.js', | ||
@@ -81,3 +82,3 @@ 'src/match/Url.js' | ||
}, | ||
uglify: { | ||
@@ -92,3 +93,3 @@ production: { | ||
}, | ||
jsduck: { | ||
@@ -100,6 +101,6 @@ main: { | ||
], | ||
// docs output dir | ||
dest: 'gh-pages/docs', | ||
// extra options | ||
@@ -130,7 +131,7 @@ options: { | ||
grunt.loadNpmTasks( 'grunt-umd' ); | ||
/** | ||
* Creates the banner comment with license header that is placed over the concatenated/minified files. | ||
* | ||
* | ||
* @private | ||
@@ -137,0 +138,0 @@ * @return {String} |
{ | ||
"name": "autolinker", | ||
"version": "0.15.3", | ||
"version": "0.16.0", | ||
"description": "Utility to automatically link the URLs, email addresses, and Twitter handles in a given block of text/HTML", | ||
@@ -5,0 +5,0 @@ "main": "dist/Autolinker.js", |
113
README.md
# Autolinker.js | ||
Because I had so much trouble finding a good autolinking implementation out in the wild, I decided to roll my own. It | ||
seemed that everything I found out there was either an implementation that didn't cover every case, or was just limited | ||
in one way or another. | ||
Because I had so much trouble finding a good auto-linking implementation out in | ||
the wild, I decided to roll my own. It seemed that everything I found out there | ||
was either an implementation that didn't cover every case, or was just limited | ||
in one way or another. | ||
So, this utility attempts to handle everything. It: | ||
- Autolinks URLs, whether or not they start with the protocol (i.e. 'http://'). In other words, it will automatically link the | ||
text "google.com", as well as "http://google.com". | ||
- Autolinks URLs, whether or not they start with the protocol (i.e. 'http://'). | ||
In other words, it will automatically link the text "google.com", as well as | ||
"http://google.com". | ||
- Will properly handle URLs with special characters | ||
- Will properly handle URLs with query parameters or a named anchor (i.e. hash) | ||
- Will autolink email addresses. | ||
- Will autolink phone numbers. | ||
- Will autolink Twitter handles. | ||
- Will properly handle HTML input. The utility will not change the `href` attribute inside anchor (<a>) tags (or any other | ||
tag/attribute for that matter), and will not accidentally wrap the inner text of an anchor tag with a new one (which would cause | ||
doubly-nested anchor tags). | ||
- Will properly handle HTML input. The utility will not change the `href` | ||
attribute inside anchor (<a>) tags (or any other tag/attribute for that | ||
matter), and will not accidentally wrap the inner text of an anchor tag with a | ||
new one (which would cause doubly-nested anchor tags). | ||
@@ -26,3 +30,4 @@ Hope that this utility helps you as well! | ||
Simply clone or download the zip of the project, and link to either `dist/Autolinker.js` or `dist/Autolinker.min.js` with a script tag: | ||
Simply clone or download the zip of the project, and link to either | ||
`dist/Autolinker.js` or `dist/Autolinker.min.js` with a script tag: | ||
@@ -53,4 +58,4 @@ ```html | ||
var Autolinker = require( 'autolinker' ); | ||
// note: npm wants an all-lowercase package name, but the utility is a class and should be | ||
// aliased with a capital letter | ||
// note: npm wants an all-lowercase package name, but the utility is a class and | ||
// should be aliased with a capital letter | ||
``` | ||
@@ -61,3 +66,4 @@ | ||
Using the static [link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-static-method-link) method: | ||
Using the static [link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-static-method-link) | ||
method: | ||
@@ -76,6 +82,8 @@ ```javascript | ||
Note: if using the same options to autolink multiple pieces of html/text, it is slightly more efficient to create a single | ||
Autolinker instance, and run the [link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-method-link) method repeatedly (i.e. use the "class" form above). | ||
Note: if using the same options to autolink multiple pieces of html/text, it is | ||
slightly more efficient to create a single Autolinker instance, and run the | ||
[link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-method-link) | ||
method repeatedly (i.e. use the "class" form above). | ||
#### Example: | ||
@@ -90,3 +98,4 @@ | ||
These are the options which may be specified for linking. These are specified by providing an Object as the second parameter to [Autolinker.link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-static-method-link). These include: | ||
These are the options which may be specified for linking. These are specified by | ||
providing an Object as the second parameter to [Autolinker.link()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-static-method-link). These include: | ||
@@ -103,9 +112,9 @@ - [newWindow](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-newWindow) : Boolean<br /> | ||
plus "url"/"email"/"twitter" suffixes for styling url/email/twitter links differently. | ||
For example, if this config is provided as "myLink", then: | ||
1) URL links will have the CSS classes: "myLink myLink-url"<br /> | ||
2) Email links will have the CSS classes: "myLink myLink-email", and<br /> | ||
3) Twitter links will have the CSS classes: "myLink myLink-twitter"<br /> | ||
- [urls](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-urls) : Boolean<br /> | ||
@@ -115,6 +124,8 @@ `true` to have URLs auto-linked, `false` to skip auto-linking of URLs. Defaults to `true`.<br /> | ||
`true` to have email addresses auto-linked, `false` to skip auto-linking of email addresses. Defaults to `true`.<br /><br /> | ||
- [phone](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-phone) : Boolean<br /> | ||
`true` to have phone numbers auto-linked, `false` to skip auto-linking of phone numbers. Defaults to `true`.<br /><br /> | ||
- [twitter](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-twitter) : Boolean<br /> | ||
`true` to have Twitter handles auto-linked, `false` to skip auto-linking of Twitter handles. Defaults to `true`.<br /><br /> | ||
- [replaceFn](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-replaceFn) : Function<br /> | ||
A function to use to programmatically make replacements of matches in the input string, one at a time. See the section | ||
A function to use to programmatically make replacements of matches in the input string, one at a time. See the section | ||
<a href="#custom-replacement-function">Custom Replacement Function</a> for more details. | ||
@@ -138,3 +149,4 @@ | ||
## More Examples | ||
One could update an entire DOM element that has unlinked text to auto-link them as such: | ||
One could update an entire DOM element that has unlinked text to auto-link them | ||
as such: | ||
@@ -146,3 +158,4 @@ ```javascript | ||
Using the same pre-configured [Autolinker](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker) instance in multiple locations of a codebase (usually by dependency injection): | ||
Using the same pre-configured [Autolinker](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker) | ||
instance in multiple locations of a codebase (usually by dependency injection): | ||
@@ -167,4 +180,5 @@ ```javascript | ||
A custom replacement function ([replaceFn](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-replaceFn)) may be provided to replace url/email/twitter matches on an individual basis, based | ||
on the return from this function. | ||
A custom replacement function ([replaceFn](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-cfg-replaceFn)) | ||
may be provided to replace url/email/twitter matches on an individual basis, | ||
based on the return from this function. | ||
@@ -180,7 +194,7 @@ Full example, for purposes of documenting the API: | ||
console.log( "text = ", match.getAnchorText() ); | ||
switch( match.getType() ) { | ||
case 'url' : | ||
case 'url' : | ||
console.log( "url: ", match.getUrl() ); | ||
if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) { | ||
@@ -190,13 +204,13 @@ var tag = autolinker.getTagBuilder().build( match ); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes | ||
tag.addClass( 'external-link' ); | ||
return tag; | ||
} else { | ||
return true; // let Autolinker perform its normal anchor tag replacement | ||
} | ||
case 'email' : | ||
var email = match.getEmail(); | ||
console.log( "email: ", email ); | ||
if( email === "my@own.address" ) { | ||
@@ -207,7 +221,13 @@ return false; // don't auto-link this particular email address; leave as-is | ||
} | ||
case 'phone' : | ||
var phoneNumber = match.getPhoneNumber(); | ||
console.log( twitterHandle ); | ||
return '<a href="http://newplace.to.link.twitter.handles.to/">' + twitterHandle + '</a>'; | ||
case 'twitter' : | ||
var twitterHandle = match.getTwitterHandle(); | ||
console.log( twitterHandle ); | ||
return '<a href="http://newplace.to.link.twitter.handles.to/">' + twitterHandle + '</a>'; | ||
@@ -222,14 +242,20 @@ } | ||
1. The Autolinker instance that is performing replacements. This can be used to query the options that the Autolinker | ||
instance is configured with, or to retrieve its TagBuilder instance (via [autolinker.getTagBuilder()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-method-getTagBuilder)). | ||
2. An [Autolinker.match.Match](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker.match.Match) object which details the match that is to be replaced. | ||
1. The Autolinker instance that is performing replacements. This can be used to | ||
query the options that the Autolinker instance is configured with, or to | ||
retrieve its TagBuilder instance (via [autolinker.getTagBuilder()](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker-method-getTagBuilder)). | ||
2. An [Autolinker.match.Match](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker.match.Match) | ||
object which details the match that is to be replaced. | ||
A replacement of the match is made based on the return value of the function. The following return values may be provided: | ||
- No return value (`undefined`), or `true` (Boolean): Delegate back to Autolinker to replace the match as it normally would. | ||
A replacement of the match is made based on the return value of the function. | ||
The following return values may be provided: | ||
- No return value (`undefined`), or `true` (Boolean): Delegate back to | ||
Autolinker to replace the match as it normally would. | ||
- `false` (Boolean): Do not replace the current match at all - leave as-is. | ||
- Any String: If a string is returned from the function, the string will be used directly as the replacement HTML for | ||
the match. | ||
- An [Autolinker.HtmlTag](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker.HtmlTag) instance, which can be used to build/modify an HTML tag before writing out its HTML text. | ||
- Any String: If a string is returned from the function, the string will be used | ||
directly as the replacement HTML for the match. | ||
- An [Autolinker.HtmlTag](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker.HtmlTag) | ||
instance, which can be used to build/modify an HTML tag before writing out its | ||
HTML text. | ||
@@ -239,3 +265,4 @@ | ||
The full API docs for Autolinker may be referenced at: [http://gregjacobs.github.io/Autolinker.js/docs/](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker) | ||
The full API docs for Autolinker may be referenced at: | ||
[http://gregjacobs.github.io/Autolinker.js/docs/](http://gregjacobs.github.io/Autolinker.js/docs/#!/api/Autolinker) | ||
@@ -242,0 +269,0 @@ |
/** | ||
* @class Autolinker | ||
* @extends Object | ||
* | ||
* Utility class used to process a given string of text, and wrap the URLs, email addresses, and Twitter handles in | ||
* | ||
* Utility class used to process a given string of text, and wrap the URLs, email addresses, phone #s, and Twitter handles in | ||
* the appropriate anchor (<a>) tags to turn them into links. | ||
* | ||
* | ||
* Any of the configuration options may be provided in an Object (map) provided to the Autolinker constructor, which | ||
* will configure how the {@link #link link()} method will process the links. | ||
* | ||
* | ||
* For example: | ||
* | ||
* | ||
* var autolinker = new Autolinker( { | ||
@@ -17,10 +17,10 @@ * newWindow : false, | ||
* } ); | ||
* | ||
* | ||
* var html = autolinker.link( "Joe went to www.yahoo.com" ); | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* | ||
* | ||
* | ||
* The {@link #static-link static link()} method may also be used to inline options into a single call, which may | ||
* be more convenient for one-off uses. For example: | ||
* | ||
* | ||
* var html = Autolinker.link( "Joe went to www.yahoo.com", { | ||
@@ -31,13 +31,13 @@ * newWindow : false, | ||
* // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>' | ||
* | ||
* | ||
* | ||
* | ||
* ## Custom Replacements of Links | ||
* | ||
* | ||
* If the configuration options do not provide enough flexibility, a {@link #replaceFn} may be provided to fully customize | ||
* the output of Autolinker. This function is called once for each URL/Email/Twitter handle match that is encountered. | ||
* | ||
* the output of Autolinker. This function is called once for each URL/Email/Phone#/Twitter handle match that is encountered. | ||
* | ||
* For example: | ||
* | ||
* var input = "..."; // string with URLs, Email Addresses, and Twitter Handles | ||
* | ||
* | ||
* var input = "..."; // string with URLs, Email Addresses, Phone #s, and Twitter Handles | ||
* | ||
* var linkedText = Autolinker.link( input, { | ||
@@ -47,7 +47,7 @@ * replaceFn : function( autolinker, match ) { | ||
* console.log( "text = ", match.getAnchorText() ); | ||
* | ||
* | ||
* switch( match.getType() ) { | ||
* case 'url' : | ||
* case 'url' : | ||
* console.log( "url: ", match.getUrl() ); | ||
* | ||
* | ||
* if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) { | ||
@@ -57,13 +57,13 @@ * var tag = autolinker.getTagBuilder().build( match ); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes | ||
* tag.addClass( 'external-link' ); | ||
* | ||
* | ||
* return tag; | ||
* | ||
* | ||
* } else { | ||
* return true; // let Autolinker perform its normal anchor tag replacement | ||
* } | ||
* | ||
* | ||
* case 'email' : | ||
* var email = match.getEmail(); | ||
* console.log( "email: ", email ); | ||
* | ||
* | ||
* if( email === "my@own.address" ) { | ||
@@ -74,7 +74,7 @@ * return false; // don't auto-link this particular email address; leave as-is | ||
* } | ||
* | ||
* | ||
* case 'twitter' : | ||
* var twitterHandle = match.getTwitterHandle(); | ||
* console.log( twitterHandle ); | ||
* | ||
* | ||
* return '<a href="http://newplace.to.link.twitter.handles.to/">' + twitterHandle + '</a>'; | ||
@@ -84,6 +84,6 @@ * } | ||
* } ); | ||
* | ||
* | ||
* | ||
* | ||
* The function may return the following values: | ||
* | ||
* | ||
* - `true` (Boolean): Allow Autolinker to replace the match as it normally would. | ||
@@ -94,3 +94,3 @@ * - `false` (Boolean): Do not replace the current match at all - leave as-is. | ||
* - An {@link Autolinker.HtmlTag} instance, which can be used to build/modify an HTML tag before writing out its HTML text. | ||
* | ||
* | ||
* @constructor | ||
@@ -103,49 +103,55 @@ * @param {Object} [config] The configuration options for the Autolinker instance, specified in an Object (map). | ||
Autolinker.prototype = { | ||
constructor : Autolinker, // fix constructor property | ||
/** | ||
* @cfg {Boolean} urls | ||
* | ||
* | ||
* `true` if miscellaneous URLs should be automatically linked, `false` if they should not be. | ||
*/ | ||
urls : true, | ||
/** | ||
* @cfg {Boolean} email | ||
* | ||
* | ||
* `true` if email addresses should be automatically linked, `false` if they should not be. | ||
*/ | ||
email : true, | ||
/** | ||
* @cfg {Boolean} twitter | ||
* | ||
* | ||
* `true` if Twitter handles ("@example") should be automatically linked, `false` if they should not be. | ||
*/ | ||
twitter : true, | ||
/** | ||
* @cfg {Boolean} phone | ||
* | ||
* `true` if Phone numbers ("(555)555-5555") should be automatically linked, `false` if they should not be. | ||
*/ | ||
phone: true, | ||
/** | ||
* @cfg {Boolean} newWindow | ||
* | ||
* | ||
* `true` if the links should open in a new window, `false` otherwise. | ||
*/ | ||
newWindow : true, | ||
/** | ||
* @cfg {Boolean} stripPrefix | ||
* | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of URL links' text, | ||
* | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of URL links' text, | ||
* `false` otherwise. | ||
*/ | ||
stripPrefix : true, | ||
/** | ||
* @cfg {Number} truncate | ||
* | ||
* A number for how many characters long URLs/emails/twitter handles should be truncated to inside the text of | ||
* a link. If the URL/email/twitter is over this number of characters, it will be truncated to this length by | ||
* | ||
* A number for how many characters long URLs/emails/phone#s/twitter handles should be truncated to inside the text of | ||
* a link. If the URL/email/phone#/twitter is over this number of characters, it will be truncated to this length by | ||
* adding a two period ellipsis ('..') to the end of the string. | ||
* | ||
* | ||
* For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file' truncated to 25 characters might look | ||
@@ -155,38 +161,39 @@ * something like this: 'yahoo.com/some/long/pat..' | ||
truncate : undefined, | ||
/** | ||
* @cfg {String} className | ||
* | ||
* | ||
* A CSS class name to add to the generated links. This class will be added to all links, as well as this class | ||
* plus url/email/twitter suffixes for styling url/email/twitter links differently. | ||
* | ||
* | ||
* For example, if this config is provided as "myLink", then: | ||
* | ||
* | ||
* - URL links will have the CSS classes: "myLink myLink-url" | ||
* - Email links will have the CSS classes: "myLink myLink-email", and | ||
* - Twitter links will have the CSS classes: "myLink myLink-twitter" | ||
* - Phone links will have the CSS classes: "myLink myLink-phone" | ||
*/ | ||
className : "", | ||
/** | ||
* @cfg {Function} replaceFn | ||
* | ||
* A function to individually process each URL/Email/Twitter match found in the input string. | ||
* | ||
* | ||
* A function to individually process each URL/Email/Twitter/Phone match found in the input string. | ||
* | ||
* See the class's description for usage. | ||
* | ||
* | ||
* This function is called with the following parameters: | ||
* | ||
* | ||
* @cfg {Autolinker} replaceFn.autolinker The Autolinker instance, which may be used to retrieve child objects from (such | ||
* as the instance's {@link #getTagBuilder tag builder}). | ||
* @cfg {Autolinker.match.Match} replaceFn.match The Match instance which can be used to retrieve information about the | ||
* {@link Autolinker.match.Url URL}/{@link Autolinker.match.Email email}/{@link Autolinker.match.Twitter Twitter} | ||
* {@link Autolinker.match.Url URL}/{@link Autolinker.match.Email email}/{@link Autolinker.match.Phone phone}/{@link Autolinker.match.Twitter Twitter} | ||
* match that the `replaceFn` is currently processing. | ||
*/ | ||
/** | ||
* @private | ||
* @property {Autolinker.htmlParser.HtmlParser} htmlParser | ||
* | ||
* | ||
* The HtmlParser instance used to skip over HTML tags, while finding text nodes to process. This is lazily instantiated | ||
@@ -196,7 +203,7 @@ * in the {@link #getHtmlParser} method. | ||
htmlParser : undefined, | ||
/** | ||
* @private | ||
* @property {Autolinker.matchParser.MatchParser} matchParser | ||
* | ||
* | ||
* The MatchParser instance used to find URL/email/Twitter matches in the text nodes of an input string passed to | ||
@@ -206,26 +213,25 @@ * {@link #link}. This is lazily instantiated in the {@link #getMatchParser} method. | ||
matchParser : undefined, | ||
/** | ||
* @private | ||
* @property {Autolinker.AnchorTagBuilder} tagBuilder | ||
* | ||
* The AnchorTagBuilder instance used to build the URL/email/Twitter replacement anchor tags. This is lazily instantiated | ||
* | ||
* The AnchorTagBuilder instance used to build the URL/email/phone/Twitter replacement anchor tags. This is lazily instantiated | ||
* in the {@link #getTagBuilder} method. | ||
*/ | ||
tagBuilder : undefined, | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Automatically links URLs, email addresses, phone numbers, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* | ||
* This method finds the text around any HTML elements in the input `textOrHtml`, which will be the text that is processed. | ||
* Any original HTML elements will be left as-is, as well as the text that is already wrapped in anchor (<a>) tags. | ||
* | ||
* @param {String} textOrHtml The HTML or text to link URLs, email addresses, and Twitter handles within (depending on if | ||
* the {@link #urls}, {@link #email}, and {@link #twitter} options are enabled). | ||
* @return {String} The HTML, with URLs/emails/Twitter handles automatically linked. | ||
* | ||
* @param {String} textOrHtml The HTML or text to link URLs, email addresses, phone numbers, and Twitter handles within (depending on if | ||
* the {@link #urls}, {@link #email}, {@link #phone}, and {@link #twitter} options are enabled). | ||
* @return {String} The HTML, with URLs/emails/phone#s/Twitter handles automatically linked. | ||
*/ | ||
@@ -237,3 +243,3 @@ link : function( textOrHtml ) { | ||
resultHtml = []; | ||
for( var i = 0, len = htmlNodes.length; i < len; i++ ) { | ||
@@ -243,3 +249,3 @@ var node = htmlNodes[ i ], | ||
nodeText = node.getText(); | ||
if( nodeType === 'element' ) { | ||
@@ -255,6 +261,6 @@ // Process HTML nodes in the input `textOrHtml` | ||
resultHtml.push( nodeText ); // now add the text of the tag itself verbatim | ||
} else if( nodeType === 'entity' ) { | ||
resultHtml.push( nodeText ); // append HTML entity nodes (such as ' ') verbatim | ||
} else { | ||
@@ -266,5 +272,5 @@ // Process text nodes in the input `textOrHtml` | ||
resultHtml.push( linkifiedStr ); | ||
} else { | ||
// `text` is within an <a> tag, simply append the text - we do not want to autolink anything | ||
// `text` is within an <a> tag, simply append the text - we do not want to autolink anything | ||
// already within an <a>...</a> tag | ||
@@ -275,13 +281,12 @@ resultHtml.push( nodeText ); | ||
} | ||
return resultHtml.join( "" ); | ||
}, | ||
/** | ||
* Process the text that lies in between HTML tags, performing the anchor tag replacements for matched | ||
* URLs/emails/Twitter handles, and returns the string with the replacements made. | ||
* | ||
* This method does the actual wrapping of URLs/emails/Twitter handles with anchor tags. | ||
* | ||
* Process the text that lies in between HTML tags, performing the anchor tag replacements for matched | ||
* URLs/emails/phone#s/Twitter handles, and returns the string with the replacements made. | ||
* | ||
* This method does the actual wrapping of URLs/emails/phone#s/Twitter handles with anchor tags. | ||
* | ||
* @private | ||
@@ -292,11 +297,11 @@ * @param {String} str The string of text to auto-link. | ||
linkifyStr : function( str ) { | ||
return this.getMatchParser().replace( str, this.createMatchReturnVal, this ); | ||
return this.getMatchParser().replace( str, this.createMatchReturnVal, this ); | ||
}, | ||
/** | ||
* Creates the return string value for a given match in the input string, for the {@link #processTextNode} method. | ||
* | ||
* | ||
* This method handles the {@link #replaceFn}, if one was provided. | ||
* | ||
* | ||
* @private | ||
@@ -313,12 +318,12 @@ * @param {Autolinker.match.Match} match The Match object that represents the match. | ||
} | ||
if( typeof replaceFnResult === 'string' ) { | ||
return replaceFnResult; // `replaceFn` returned a string, use that | ||
} else if( replaceFnResult === false ) { | ||
return match.getMatchedText(); // no replacement for the match | ||
} else if( replaceFnResult instanceof Autolinker.HtmlTag ) { | ||
return replaceFnResult.toString(); | ||
return replaceFnResult.toAnchorString(); | ||
} else { // replaceFnResult === true, or no/unknown return value from function | ||
@@ -328,11 +333,11 @@ // Perform Autolinker's default anchor tag generation | ||
anchorTag = tagBuilder.build( match ); // returns an Autolinker.HtmlTag instance | ||
return anchorTag.toString(); | ||
return anchorTag.toAnchorString(); | ||
} | ||
}, | ||
/** | ||
* Lazily instantiates and returns the {@link #htmlParser} instance for this Autolinker instance. | ||
* | ||
* | ||
* @protected | ||
@@ -343,14 +348,14 @@ * @return {Autolinker.htmlParser.HtmlParser} | ||
var htmlParser = this.htmlParser; | ||
if( !htmlParser ) { | ||
htmlParser = this.htmlParser = new Autolinker.htmlParser.HtmlParser(); | ||
} | ||
return htmlParser; | ||
}, | ||
/** | ||
* Lazily instantiates and returns the {@link #matchParser} instance for this Autolinker instance. | ||
* | ||
* | ||
* @protected | ||
@@ -361,3 +366,3 @@ * @return {Autolinker.matchParser.MatchParser} | ||
var matchParser = this.matchParser; | ||
if( !matchParser ) { | ||
@@ -368,17 +373,18 @@ matchParser = this.matchParser = new Autolinker.matchParser.MatchParser( { | ||
twitter : this.twitter, | ||
phone: this.phone, | ||
stripPrefix : this.stripPrefix | ||
} ); | ||
} | ||
return matchParser; | ||
}, | ||
/** | ||
* Returns the {@link #tagBuilder} instance for this Autolinker instance, lazily instantiating it | ||
* if it does not yet exist. | ||
* | ||
* This method may be used in a {@link #replaceFn} to generate the {@link Autolinker.HtmlTag HtmlTag} instance that | ||
* | ||
* This method may be used in a {@link #replaceFn} to generate the {@link Autolinker.HtmlTag HtmlTag} instance that | ||
* Autolinker would normally generate, and then allow for modifications before returning it. For example: | ||
* | ||
* | ||
* var html = Autolinker.link( "Test google.com", { | ||
@@ -388,10 +394,10 @@ * replaceFn : function( autolinker, match ) { | ||
* tag.setAttr( 'rel', 'nofollow' ); | ||
* | ||
* | ||
* return tag; | ||
* } | ||
* } ); | ||
* | ||
* | ||
* // generated html: | ||
* // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a> | ||
* | ||
* | ||
* @return {Autolinker.AnchorTagBuilder} | ||
@@ -401,3 +407,3 @@ */ | ||
var tagBuilder = this.tagBuilder; | ||
if( !tagBuilder ) { | ||
@@ -410,3 +416,3 @@ tagBuilder = this.tagBuilder = new Autolinker.AnchorTagBuilder( { | ||
} | ||
return tagBuilder; | ||
@@ -419,15 +425,15 @@ } | ||
/** | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Automatically links URLs, email addresses, and Twitter handles found in the given chunk of HTML. | ||
* Does not link URLs found within HTML tags. | ||
* | ||
* | ||
* For instance, if given the text: `You should go to http://www.yahoo.com`, then the result | ||
* will be `You should go to <a href="http://www.yahoo.com">http://www.yahoo.com</a>` | ||
* | ||
* | ||
* Example: | ||
* | ||
* | ||
* var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } ); | ||
* // Produces: "Go to <a href="http://google.com">google.com</a>" | ||
* | ||
* | ||
* @static | ||
* @param {String} textOrHtml The HTML or text to find URLs, email addresses, and Twitter handles within (depending on if | ||
* @param {String} textOrHtml The HTML or text to find URLs, email addresses, phone #s, and Twitter handles within (depending on if | ||
* the {@link #urls}, {@link #email}, and {@link #twitter} options are enabled). | ||
@@ -434,0 +440,0 @@ * @param {Object} [options] Any of the configuration options for the Autolinker class, specified in an Object (map). |
@@ -6,12 +6,12 @@ /*global Autolinker */ | ||
* @extends Object | ||
* | ||
* | ||
* Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically. | ||
* | ||
* | ||
* Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use | ||
* this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}. | ||
* | ||
* | ||
* ## Examples | ||
* | ||
* | ||
* Example instantiation: | ||
* | ||
* | ||
* var tag = new Autolinker.HtmlTag( { | ||
@@ -22,5 +22,5 @@ * tagName : 'a', | ||
* } ); | ||
* | ||
* tag.toString(); // <a href="http://google.com" class="external-link">Google</a> | ||
* | ||
* | ||
* tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a> | ||
* | ||
* // Individual accessor methods | ||
@@ -30,6 +30,6 @@ * tag.getTagName(); // 'a' | ||
* tag.hasClass( 'external-link' ); // true | ||
* | ||
* | ||
* | ||
* | ||
* Using mutator methods (which may be used in combination with instantiation config properties): | ||
* | ||
* | ||
* var tag = new Autolinker.HtmlTag(); | ||
@@ -40,12 +40,12 @@ * tag.setTagName( 'a' ); | ||
* tag.setInnerHtml( 'Google' ); | ||
* | ||
* | ||
* tag.getTagName(); // 'a' | ||
* tag.getAttr( 'href' ); // 'http://google.com' | ||
* tag.hasClass( 'external-link' ); // true | ||
* | ||
* tag.toString(); // <a href="http://google.com" class="external-link">Google</a> | ||
* | ||
* | ||
* | ||
* tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a> | ||
* | ||
* | ||
* ## Example use within a {@link Autolinker#replaceFn replaceFn} | ||
* | ||
* | ||
* var html = Autolinker.link( "Test google.com", { | ||
@@ -55,13 +55,13 @@ * replaceFn : function( autolinker, match ) { | ||
* tag.setAttr( 'rel', 'nofollow' ); | ||
* | ||
* | ||
* return tag; | ||
* } | ||
* } ); | ||
* | ||
* | ||
* // generated html: | ||
* // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a> | ||
* | ||
* | ||
* | ||
* | ||
* ## Example use with a new tag for the replacement | ||
* | ||
* | ||
* var html = Autolinker.link( "Test google.com", { | ||
@@ -74,7 +74,7 @@ * replaceFn : function( autolinker, match ) { | ||
* } ); | ||
* | ||
* | ||
* return tag; | ||
* } | ||
* } ); | ||
* | ||
* | ||
* // generated html: | ||
@@ -84,46 +84,46 @@ * // Test <button title="Load URL: http://google.com">Load URL: google.com</button> | ||
Autolinker.HtmlTag = Autolinker.Util.extend( Object, { | ||
/** | ||
* @cfg {String} tagName | ||
* | ||
* | ||
* The tag name. Ex: 'a', 'button', etc. | ||
* | ||
* Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toString} | ||
* | ||
* Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString} | ||
* is executed. | ||
*/ | ||
/** | ||
* @cfg {Object.<String, String>} attrs | ||
* | ||
* | ||
* An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the | ||
* values are the attribute values. | ||
*/ | ||
/** | ||
* @cfg {String} innerHtml | ||
* | ||
* The inner HTML for the tag. | ||
* | ||
* Note the camel case name on `innerHtml`. Acronyms are camelCased in this utility (such as not to run into the acronym | ||
* | ||
* The inner HTML for the tag. | ||
* | ||
* Note the camel case name on `innerHtml`. Acronyms are camelCased in this utility (such as not to run into the acronym | ||
* naming inconsistency that the DOM developers created with `XMLHttpRequest`). You may alternatively use {@link #innerHTML} | ||
* if you prefer, but this one is recommended. | ||
*/ | ||
/** | ||
* @cfg {String} innerHTML | ||
* | ||
* | ||
* Alias of {@link #innerHtml}, accepted for consistency with the browser DOM api, but prefer the camelCased version | ||
* for acronym names. | ||
*/ | ||
/** | ||
* @protected | ||
* @property {RegExp} whitespaceRegex | ||
* | ||
* | ||
* Regular expression used to match whitespace in a string of CSS classes. | ||
*/ | ||
whitespaceRegex : /\s+/, | ||
/** | ||
@@ -135,10 +135,10 @@ * @constructor | ||
Autolinker.Util.assign( this, cfg ); | ||
this.innerHtml = this.innerHtml || this.innerHTML; // accept either the camelCased form or the fully capitalized acronym | ||
}, | ||
/** | ||
* Sets the tag name that will be used to generate the tag with. | ||
* | ||
* | ||
* @param {String} tagName | ||
@@ -151,7 +151,7 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
}, | ||
/** | ||
* Retrieves the tag name. | ||
* | ||
* | ||
* @return {String} | ||
@@ -162,7 +162,7 @@ */ | ||
}, | ||
/** | ||
* Sets an attribute on the HtmlTag. | ||
* | ||
* | ||
* @param {String} attrName The attribute name to set. | ||
@@ -175,10 +175,10 @@ * @param {String} attrValue The attribute value to set. | ||
tagAttrs[ attrName ] = attrValue; | ||
return this; | ||
}, | ||
/** | ||
* Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`. | ||
* | ||
* | ||
* @param {String} name The attribute name to retrieve. | ||
@@ -190,7 +190,7 @@ * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag. | ||
}, | ||
/** | ||
* Sets one or more attributes on the HtmlTag. | ||
* | ||
* | ||
* @param {Object.<String, String>} attrs A key/value Object (map) of the attributes to set. | ||
@@ -202,10 +202,10 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
Autolinker.Util.assign( tagAttrs, attrs ); | ||
return this; | ||
}, | ||
/** | ||
* Retrieves the attributes Object (map) for the HtmlTag. | ||
* | ||
* | ||
* @return {Object.<String, String>} A key/value object of the attributes for the HtmlTag. | ||
@@ -216,7 +216,7 @@ */ | ||
}, | ||
/** | ||
* Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag. | ||
* | ||
* | ||
* @param {String} cssClass One or more space-separated CSS classes to set (overwrite). | ||
@@ -228,7 +228,7 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
}, | ||
/** | ||
* Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes. | ||
* | ||
* | ||
* @param {String} cssClass One or more space-separated CSS classes to add. | ||
@@ -244,3 +244,3 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
newClass; | ||
while( newClass = newClasses.shift() ) { | ||
@@ -251,11 +251,11 @@ if( indexOf( classes, newClass ) === -1 ) { | ||
} | ||
this.getAttrs()[ 'class' ] = classes.join( " " ); | ||
return this; | ||
}, | ||
/** | ||
* Convenience method to remove one or more CSS classes from the HtmlTag. | ||
* | ||
* | ||
* @param {String} cssClass One or more space-separated CSS classes to remove. | ||
@@ -271,3 +271,3 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
removeClass; | ||
while( classes.length && ( removeClass = removeClasses.shift() ) ) { | ||
@@ -279,12 +279,12 @@ var idx = indexOf( classes, removeClass ); | ||
} | ||
this.getAttrs()[ 'class' ] = classes.join( " " ); | ||
return this; | ||
}, | ||
/** | ||
* Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when | ||
* there are multiple. | ||
* | ||
* | ||
* @return {String} | ||
@@ -295,7 +295,7 @@ */ | ||
}, | ||
/** | ||
* Convenience method to check if the tag has a CSS class or not. | ||
* | ||
* | ||
* @param {String} cssClass The CSS class to check for. | ||
@@ -307,7 +307,7 @@ * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise. | ||
}, | ||
/** | ||
* Sets the inner HTML for the tag. | ||
* | ||
* | ||
* @param {String} html The inner HTML to set. | ||
@@ -318,10 +318,10 @@ * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. | ||
this.innerHtml = html; | ||
return this; | ||
}, | ||
/** | ||
* Retrieves the inner HTML for the tag. | ||
* | ||
* | ||
* @return {String} | ||
@@ -332,23 +332,23 @@ */ | ||
}, | ||
/** | ||
* Override of superclass method used to generate the HTML string for the tag. | ||
* | ||
* | ||
* @return {String} | ||
*/ | ||
toString : function() { | ||
toAnchorString : function() { | ||
var tagName = this.getTagName(), | ||
attrsStr = this.buildAttrsStr(); | ||
attrsStr = ( attrsStr ) ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes | ||
return [ '<', tagName, attrsStr, '>', this.getInnerHtml(), '</', tagName, '>' ].join( "" ); | ||
}, | ||
/** | ||
* Support method for {@link #toString}, returns the string space-separated key="value" pairs, used to populate | ||
* Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate | ||
* the stringified HtmlTag. | ||
* | ||
* | ||
* @protected | ||
@@ -359,6 +359,6 @@ * @return {String} Example return: `attr1="value1" attr2="value2"` | ||
if( !this.attrs ) return ""; // no `attrs` Object (map) has been set, return empty string | ||
var attrs = this.getAttrs(), | ||
attrsArr = []; | ||
for( var prop in attrs ) { | ||
@@ -371,3 +371,3 @@ if( attrs.hasOwnProperty( prop ) ) { | ||
} | ||
} ); | ||
} ); |
@@ -6,93 +6,119 @@ /*global Autolinker */ | ||
* @extends Object | ||
* | ||
* Used by Autolinker to parse {@link #urls URLs}, {@link #emails email addresses}, and {@link #twitter Twitter handles}, | ||
* given an input string of text. | ||
* | ||
* The MatchParser is fed a non-HTML string in order to search out URLs, email addresses and Twitter handles. Autolinker | ||
* first uses the {@link HtmlParser} to "walk around" HTML tags, and then the text around the HTML tags is passed into | ||
* the MatchParser in order to find the actual matches. | ||
* | ||
* Used by Autolinker to parse {@link #urls URLs}, {@link #emails email addresses}, | ||
* and {@link #twitter Twitter handles}, given an input string of text. | ||
* | ||
* The MatchParser is fed a non-HTML string in order to search out URLs, email | ||
* addresses and Twitter handles. Autolinker first uses the {@link HtmlParser} | ||
* to "walk around" HTML tags, and then the text around the HTML tags is passed | ||
* into the MatchParser in order to find the actual matches. | ||
*/ | ||
Autolinker.matchParser.MatchParser = Autolinker.Util.extend( Object, { | ||
/** | ||
* @cfg {Boolean} urls | ||
* | ||
* `true` if miscellaneous URLs should be automatically linked, `false` if they should not be. | ||
* | ||
* `true` if miscellaneous URLs should be automatically linked, `false` if | ||
* they should not. | ||
*/ | ||
urls : true, | ||
/** | ||
* @cfg {Boolean} email | ||
* | ||
* `true` if email addresses should be automatically linked, `false` if they should not be. | ||
* | ||
* `true` if email addresses should be automatically linked, `false` if they | ||
* should not. | ||
*/ | ||
email : true, | ||
/** | ||
* @cfg {Boolean} twitter | ||
* | ||
* `true` if Twitter handles ("@example") should be automatically linked, `false` if they should not be. | ||
* | ||
* `true` if Twitter handles ("@example") should be automatically linked, | ||
* `false` if they should not. | ||
*/ | ||
twitter : true, | ||
/** | ||
* @cfg {Boolean} phone | ||
* | ||
* `true` if Phone numbers ("(555)555-5555") should be automatically linked, | ||
* `false` if they should not. | ||
*/ | ||
phone: true, | ||
/** | ||
* @cfg {Boolean} stripPrefix | ||
* | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped from the beginning of URL links' text | ||
* in {@link Autolinker.match.Url URL matches}, `false` otherwise. | ||
* | ||
* | ||
* `true` if 'http://' or 'https://' and/or the 'www.' should be stripped | ||
* from the beginning of URL links' text in {@link Autolinker.match.Url URL matches}, | ||
* `false` otherwise. | ||
* | ||
* TODO: Handle this before a URL Match object is instantiated. | ||
*/ | ||
stripPrefix : true, | ||
/** | ||
* @private | ||
* @property {RegExp} matcherRegex | ||
* | ||
* The regular expression that matches URLs, email addresses, and Twitter handles. | ||
* | ||
* | ||
* The regular expression that matches URLs, email addresses, phone #s, and | ||
* Twitter handles. | ||
* | ||
* This regular expression has the following capturing groups: | ||
* | ||
* 1. Group that is used to determine if there is a Twitter handle match (i.e. \@someTwitterUser). Simply check for its | ||
* existence to determine if there is a Twitter handle match. The next couple of capturing groups give information | ||
* about the Twitter handle match. | ||
* 2. The whitespace character before the \@sign in a Twitter handle. This is needed because there are no lookbehinds in | ||
* JS regular expressions, and can be used to reconstruct the original string in a replace(). | ||
* 3. The Twitter handle itself in a Twitter match. If the match is '@someTwitterUser', the handle is 'someTwitterUser'. | ||
* 4. Group that matches an email address. Used to determine if the match is an email address, as well as holding the full | ||
* address. Ex: 'me@my.com' | ||
* 5. Group that matches a URL in the input text. Ex: 'http://google.com', 'www.google.com', or just 'google.com'. | ||
* This also includes a path, url parameters, or hash anchors. Ex: google.com/path/to/file?q1=1&q2=2#myAnchor | ||
* 6. Group that matches a protocol URL (i.e. 'http://google.com'). This is used to match protocol URLs with just a single | ||
* word, like 'http://localhost', where we won't double check that the domain name has at least one '.' in it. | ||
* 7. A protocol-relative ('//') match for the case of a 'www.' prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. We need to know the character before the '//' in order to determine if it is a valid match | ||
* or the // was in a string we don't want to auto-link. | ||
* 8. A protocol-relative ('//') match for the case of a known TLD prefixed URL. Will be an empty string if it is not a | ||
* protocol-relative match. See #6 for more info. | ||
* | ||
* 1. Group that is used to determine if there is a Twitter handle match | ||
* (i.e. \@someTwitterUser). Simply check for its existence to determine | ||
* if there is a Twitter handle match. The next couple of capturing | ||
* groups give information about the Twitter handle match. | ||
* 2. The whitespace character before the \@sign in a Twitter handle. This | ||
* is needed because there are no lookbehinds in JS regular expressions, | ||
* and can be used to reconstruct the original string in a replace(). | ||
* 3. The Twitter handle itself in a Twitter match. If the match is | ||
* '@someTwitterUser', the handle is 'someTwitterUser'. | ||
* 4. Group that matches an email address. Used to determine if the match | ||
* is an email address, as well as holding the full address. Ex: | ||
* 'me@my.com' | ||
* 5. Group that matches a URL in the input text. Ex: 'http://google.com', | ||
* 'www.google.com', or just 'google.com'. This also includes a path, | ||
* url parameters, or hash anchors. Ex: google.com/path/to/file?q1=1&q2=2#myAnchor | ||
* 6. Group that matches a protocol URL (i.e. 'http://google.com'). This is | ||
* used to match protocol URLs with just a single word, like 'http://localhost', | ||
* where we won't double check that the domain name has at least one '.' | ||
* in it. | ||
* 7. A protocol-relative ('//') match for the case of a 'www.' prefixed | ||
* URL. Will be an empty string if it is not a protocol-relative match. | ||
* We need to know the character before the '//' in order to determine | ||
* if it is a valid match or the // was in a string we don't want to | ||
* auto-link. | ||
* 8. A protocol-relative ('//') match for the case of a known TLD prefixed | ||
* URL. Will be an empty string if it is not a protocol-relative match. | ||
* See #6 for more info. | ||
* 9. Group that is used to determine if there is a phone number match. The | ||
* next 3 groups give segments of the phone number. | ||
*/ | ||
matcherRegex : (function() { | ||
var twitterRegex = /(^|[^\w])@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
var twitterRegex = /(^|[^\w])@(\w{1,15})/, // For matching a twitter handle. Ex: @gregory_jacobs | ||
emailRegex = /(?:[\-;:&=\+\$,\w\.]+@)/, // something@ for email addresses (a.k.a. local-part) | ||
phoneRegex = /(?:\+?\d{1,3}[-\s.])?\(?\d{3}\)?[-\s.]?\d{3}[-\s.]\d{4}/, // ex: (123) 456-7890, 123 456 7890, 123-456-7890, etc. | ||
protocolRegex = /(?:[A-Za-z][-.+A-Za-z0-9]+:(?![A-Za-z][-.+A-Za-z0-9]+:\/\/)(?!\d+\/?)(?:\/\/)?)/, // match protocol, allow in format "http://" or "mailto:". However, do not match the first part of something like 'link:http://www.google.com' (i.e. don't match "link:"). Also, make sure we don't interpret 'google.com:8000' as if 'google.com' was a protocol here (i.e. ignore a trailing port number in this regex) | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
wwwRegex = /(?:www\.)/, // starting with 'www.' | ||
domainNameRegex = /[A-Za-z0-9\.\-]*[A-Za-z0-9\-]/, // anything looking at all like a domain, non-unicode domains, not ending in a period | ||
tldRegex = /\.(?:international|construction|contractors|enterprises|photography|productions|foundation|immobilien|industries|management|properties|technology|christmas|community|directory|education|equipment|institute|marketing|solutions|vacations|bargains|boutique|builders|catering|cleaning|clothing|computer|democrat|diamonds|graphics|holdings|lighting|partners|plumbing|supplies|training|ventures|academy|careers|company|cruises|domains|exposed|flights|florist|gallery|guitars|holiday|kitchen|neustar|okinawa|recipes|rentals|reviews|shiksha|singles|support|systems|agency|berlin|camera|center|coffee|condos|dating|estate|events|expert|futbol|kaufen|luxury|maison|monash|museum|nagoya|photos|repair|report|social|supply|tattoo|tienda|travel|viajes|villas|vision|voting|voyage|actor|build|cards|cheap|codes|dance|email|glass|house|mango|ninja|parts|photo|shoes|solar|today|tokyo|tools|watch|works|aero|arpa|asia|best|bike|blue|buzz|camp|club|cool|coop|farm|fish|gift|guru|info|jobs|kiwi|kred|land|limo|link|menu|mobi|moda|name|pics|pink|post|qpon|rich|ruhr|sexy|tips|vote|voto|wang|wien|wiki|zone|bar|bid|biz|cab|cat|ceo|com|edu|gov|int|kim|mil|net|onl|org|pro|pub|red|tel|uno|wed|xxx|xyz|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)\b/, // match our known top level domains (TLDs) | ||
// Allow optional path, query string, and hash anchor, not ending in the following characters: "?!:,.;" | ||
// http://blog.codinghorror.com/the-problem-with-urls/ | ||
urlSuffixRegex = /[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]?!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|'$*\[\]]/; | ||
return new RegExp( [ | ||
'(', // *** Capturing group $1, which can be used to check for a twitter handle match. Use group $3 for the actual twitter handle though. $2 may be used to reconstruct the original string in a replace() | ||
// *** Capturing group $2, which matches the whitespace character before the '@' sign (needed because of no lookbehinds), and | ||
'(', // *** Capturing group $1, which can be used to check for a twitter handle match. Use group $3 for the actual twitter handle though. $2 may be used to reconstruct the original string in a replace() | ||
// *** Capturing group $2, which matches the whitespace character before the '@' sign (needed because of no lookbehinds), and | ||
// *** Capturing group $3, which matches the actual twitter handle | ||
twitterRegex.source, | ||
')', | ||
'|', | ||
'(', // *** Capturing group $4, which is used to determine an email match | ||
@@ -103,5 +129,5 @@ emailRegex.source, | ||
')', | ||
'|', | ||
'(', // *** Capturing group $5, which is used to match a URL | ||
@@ -113,5 +139,5 @@ '(?:', // parens to cover match for protocol (optional), and domain | ||
')', | ||
'|', | ||
'(?:', // non-capturing paren for a 'www.' prefixed url (ex: www.google.com) | ||
@@ -122,5 +148,5 @@ '(.?//)?', // *** Capturing group $7 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
')', | ||
'|', | ||
'(?:', // non-capturing paren for known a TLD url (ex: google.com) | ||
@@ -132,14 +158,22 @@ '(.?//)?', // *** Capturing group $8 for an optional protocol-relative URL. Must be at the beginning of the string or start with a non-word character | ||
')', | ||
'(?:' + urlSuffixRegex.source + ')?', // match for path, query string, and/or hash anchor - optional | ||
')', | ||
'|', | ||
// this setup does not scale well for open extension :( Need to rethink design of autolinker... | ||
// *** Capturing group $9, which matches a (USA for now) phone number | ||
'(', | ||
phoneRegex.source, | ||
')' | ||
].join( "" ), 'gi' ); | ||
} )(), | ||
/** | ||
* @private | ||
* @property {RegExp} charBeforeProtocolRelMatchRegex | ||
* | ||
* | ||
* The regular expression used to retrieve the character before a protocol-relative URL match. | ||
* | ||
* | ||
* This is used in conjunction with the {@link #matcherRegex}, which needs to grab the character before a protocol-relative | ||
@@ -150,12 +184,12 @@ * '//' due to the lack of a negative look-behind in JavaScript regular expressions. The character before the match is stripped | ||
charBeforeProtocolRelMatchRegex : /^(.)?\/\//, | ||
/** | ||
* @private | ||
* @property {Autolinker.MatchValidator} matchValidator | ||
* | ||
* | ||
* The MatchValidator object, used to filter out any false positives from the {@link #matcherRegex}. See | ||
* {@link Autolinker.MatchValidator} for details. | ||
*/ | ||
/** | ||
@@ -167,16 +201,18 @@ * @constructor | ||
Autolinker.Util.assign( this, cfg ); | ||
this.matchValidator = new Autolinker.MatchValidator(); | ||
}, | ||
/** | ||
* Parses the input `text` to search for URLs/emails/Twitter handles, and calls the `replaceFn` | ||
* to allow replacements of the matches. Returns the `text` with matches replaced. | ||
* | ||
* Parses the input `text` to search for URLs/emails/Twitter handles, and | ||
* calls the `replaceFn` to allow replacements of the matches. Returns the | ||
* `text` with matches replaced. | ||
* | ||
* @param {String} text The text to search and repace matches in. | ||
* @param {Function} replaceFn The iterator function to handle the replacements. The function takes a | ||
* single argument, a {@link Autolinker.match.Match} object, and should return the text that should | ||
* make the replacement. | ||
* @param {Object} [contextObj=window] The context object ("scope") to run the `replaceFn` in. | ||
* @param {Function} replaceFn The iterator function to handle the | ||
* replacements. The function takes a single argument, a {@link Autolinker.match.Match} | ||
* object, and should return the text that should make the replacement. | ||
* @param {Object} [contextObj=window] The context object ("scope") to run | ||
* the `replaceFn` in. | ||
* @return {String} | ||
@@ -186,11 +222,13 @@ */ | ||
var me = this; // for closure | ||
return text.replace( this.matcherRegex, function( matchStr, $1, $2, $3, $4, $5, $6, $7, $8 ) { | ||
var matchDescObj = me.processCandidateMatch( matchStr, $1, $2, $3, $4, $5, $6, $7, $8 ); // "match description" object | ||
// Return out with no changes for match types that are disabled (url, email, twitter), or for matches that are | ||
// invalid (false positives from the matcherRegex, which can't use look-behinds since they are unavailable in JS). | ||
return text.replace( this.matcherRegex, function( matchStr, $1, $2, $3, $4, $5, $6, $7, $8, $9 ) { | ||
var matchDescObj = me.processCandidateMatch( matchStr, $1, $2, $3, $4, $5, $6, $7, $8, $9 ); // "match description" object | ||
// Return out with no changes for match types that are disabled (url, | ||
// email, phone, twitter), or for matches that are invalid (false | ||
// positives from the matcherRegex, which can't use look-behinds | ||
// since they are unavailable in JS). | ||
if( !matchDescObj ) { | ||
return matchStr; | ||
} else { | ||
@@ -203,14 +241,14 @@ // Generate replacement text for the match from the `replaceFn` | ||
}, | ||
/** | ||
* Processes a candidate match from the {@link #matcherRegex}. | ||
* | ||
* Processes a candidate match from the {@link #matcherRegex}. | ||
* | ||
* Not all matches found by the regex are actual URL/email/Twitter matches, as determined by the {@link #matchValidator}. In | ||
* this case, the method returns `null`. Otherwise, a valid Object with `prefixStr`, `match`, and `suffixStr` is returned. | ||
* | ||
* | ||
* @private | ||
* @param {String} matchStr The full match that was found by the {@link #matcherRegex}. | ||
* @param {String} twitterMatch The matched text of a Twitter handle, if the match is a Twitter match. | ||
* @param {String} twitterHandlePrefixWhitespaceChar The whitespace char before the @ sign in a Twitter handle match. This | ||
* @param {String} twitterHandlePrefixWhitespaceChar The whitespace char before the @ sign in a Twitter handle match. This | ||
* is needed because of no lookbehinds in JS regexes, and is need to re-include the character for the anchor tag replacement. | ||
@@ -222,42 +260,42 @@ * @param {String} twitterHandle The actual Twitter user (i.e the word after the @ sign in a Twitter match). | ||
* something like 'http://localhost', where we won't double check that the domain name has at least one '.' in it. | ||
* @param {String} wwwProtocolRelativeMatch The '//' for a protocol-relative match from a 'www' url, with the character that | ||
* @param {String} wwwProtocolRelativeMatch The '//' for a protocol-relative match from a 'www' url, with the character that | ||
* comes before the '//'. | ||
* @param {String} tldProtocolRelativeMatch The '//' for a protocol-relative match from a TLD (top level domain) match, with | ||
* @param {String} tldProtocolRelativeMatch The '//' for a protocol-relative match from a TLD (top level domain) match, with | ||
* the character that comes before the '//'. | ||
* | ||
* @param {String} phoneMatch The matched text of a phone number | ||
* | ||
* @return {Object} A "match description object". This will be `null` if the match was invalid, or if a match type is disabled. | ||
* Otherwise, this will be an Object (map) with the following properties: | ||
* @return {String} return.prefixStr The char(s) that should be prepended to the replacement string. These are char(s) that | ||
* were needed to be included from the regex match that were ignored by processing code, and should be re-inserted into | ||
* were needed to be included from the regex match that were ignored by processing code, and should be re-inserted into | ||
* the replacement stream. | ||
* @return {String} return.suffixStr The char(s) that should be appended to the replacement string. These are char(s) that | ||
* were needed to be included from the regex match that were ignored by processing code, and should be re-inserted into | ||
* were needed to be included from the regex match that were ignored by processing code, and should be re-inserted into | ||
* the replacement stream. | ||
* @return {Autolinker.match.Match} return.match The Match object that represents the match that was found. | ||
*/ | ||
processCandidateMatch : function( | ||
matchStr, twitterMatch, twitterHandlePrefixWhitespaceChar, twitterHandle, | ||
emailAddressMatch, urlMatch, protocolUrlMatch, wwwProtocolRelativeMatch, tldProtocolRelativeMatch | ||
) { | ||
// Note: The `matchStr` variable wil be fixed up to remove characters that are no longer needed (which will | ||
processCandidateMatch : function( | ||
matchStr, twitterMatch, twitterHandlePrefixWhitespaceChar, twitterHandle, | ||
emailAddressMatch, urlMatch, protocolUrlMatch, wwwProtocolRelativeMatch, | ||
tldProtocolRelativeMatch, phoneMatch ) { | ||
// Note: The `matchStr` variable wil be fixed up to remove characters that are no longer needed (which will | ||
// be added to `prefixStr` and `suffixStr`). | ||
var protocolRelativeMatch = wwwProtocolRelativeMatch || tldProtocolRelativeMatch, | ||
match, // Will be an Autolinker.match.Match object | ||
prefixStr = "", // A string to use to prefix the anchor tag that is created. This is needed for the Twitter handle match | ||
suffixStr = ""; // A string to suffix the anchor tag that is created. This is used if there is a trailing parenthesis that should not be auto-linked. | ||
// Return out with `null` for match types that are disabled (url, email, twitter), or for matches that are | ||
// Return out with `null` for match types that are disabled (url, email, twitter), or for matches that are | ||
// invalid (false positives from the matcherRegex, which can't use look-behinds since they are unavailable in JS). | ||
if( | ||
( twitterMatch && !this.twitter ) || ( emailAddressMatch && !this.email ) || ( urlMatch && !this.urls ) || | ||
!this.matchValidator.isValidMatch( urlMatch, protocolUrlMatch, protocolRelativeMatch ) | ||
( twitterMatch && !this.twitter ) || ( emailAddressMatch && !this.email ) || ( urlMatch && !this.urls ) || (phoneMatch && !this.phone) || | ||
!this.matchValidator.isValidMatch( urlMatch, protocolUrlMatch, protocolRelativeMatch ) | ||
) { | ||
return null; | ||
} | ||
// Handle a closing parenthesis at the end of the match, and exclude it if there is not a matching open parenthesis | ||
// in the match itself. | ||
// in the match itself. | ||
if( this.matchHasUnbalancedClosingParen( matchStr ) ) { | ||
@@ -267,9 +305,8 @@ matchStr = matchStr.substr( 0, matchStr.length - 1 ); // remove the trailing ")" | ||
} | ||
if( emailAddressMatch ) { | ||
match = new Autolinker.match.Email( { matchedText: matchStr, email: emailAddressMatch } ); | ||
} else if( twitterMatch ) { | ||
// fix up the `matchStr` if there was a preceding whitespace char, which was needed to determine the match | ||
// fix up the `matchStr` if there was a preceding whitespace char, which was needed to determine the match | ||
// itself (since there are no look-behinds in JS regexes) | ||
@@ -281,3 +318,8 @@ if( twitterHandlePrefixWhitespaceChar ) { | ||
match = new Autolinker.match.Twitter( { matchedText: matchStr, twitterHandle: twitterHandle } ); | ||
} else if ( phoneMatch ) { | ||
// remove non-numeric values from phone number string | ||
var cleanNumber = matchStr.replace( /\D/g, '' ); | ||
match = new Autolinker.match.Phone( { matchedText: matchStr, number: cleanNumber } ); | ||
} else { // url match | ||
@@ -288,3 +330,3 @@ // If it's a protocol-relative '//' match, remove the character before the '//' (which the matcherRegex needed | ||
var charBeforeMatch = protocolRelativeMatch.match( this.charBeforeProtocolRelMatchRegex )[ 1 ] || ""; | ||
if( charBeforeMatch ) { // fix up the `matchStr` if there was a preceding char before a protocol-relative match, which was needed to determine the match itself (since there are no look-behinds in JS regexes) | ||
@@ -295,12 +337,12 @@ prefixStr = charBeforeMatch; | ||
} | ||
match = new Autolinker.match.Url( { | ||
matchedText : matchStr, | ||
url : matchStr, | ||
protocolUrlMatch : !!protocolUrlMatch, | ||
matchedText : matchStr, | ||
url : matchStr, | ||
protocolUrlMatch : !!protocolUrlMatch, | ||
protocolRelativeMatch : !!protocolRelativeMatch, | ||
stripPrefix : this.stripPrefix | ||
stripPrefix : this.stripPrefix | ||
} ); | ||
} | ||
return { | ||
@@ -312,15 +354,15 @@ prefixStr : prefixStr, | ||
}, | ||
/** | ||
* Determines if a match found has an unmatched closing parenthesis. If so, this parenthesis will be removed | ||
* from the match itself, and appended after the generated anchor tag in {@link #processTextNode}. | ||
* | ||
* | ||
* A match may have an extra closing parenthesis at the end of the match because the regular expression must include parenthesis | ||
* for URLs such as "wikipedia.com/something_(disambiguation)", which should be auto-linked. | ||
* | ||
* for URLs such as "wikipedia.com/something_(disambiguation)", which should be auto-linked. | ||
* | ||
* However, an extra parenthesis *will* be included when the URL itself is wrapped in parenthesis, such as in the case of | ||
* "(wikipedia.com/something_(disambiguation))". In this case, the last closing parenthesis should *not* be part of the URL | ||
* "(wikipedia.com/something_(disambiguation))". In this case, the last closing parenthesis should *not* be part of the URL | ||
* itself, and this method will return `true`. | ||
* | ||
* | ||
* @private | ||
@@ -332,3 +374,3 @@ * @param {String} matchStr The full match string from the {@link #matcherRegex}. | ||
var lastChar = matchStr.charAt( matchStr.length - 1 ); | ||
if( lastChar === ')' ) { | ||
@@ -339,3 +381,3 @@ var openParensMatch = matchStr.match( /\(/g ), | ||
numCloseParens = ( closeParensMatch && closeParensMatch.length ) || 0; | ||
if( numOpenParens < numCloseParens ) { | ||
@@ -345,6 +387,6 @@ return true; | ||
} | ||
return false; | ||
} | ||
} ); | ||
} ); |
@@ -339,3 +339,3 @@ /*global Autolinker, _, describe, beforeEach, afterEach, it, expect */ | ||
describe( 'toString()', function() { | ||
describe( 'toAnchorString()', function() { | ||
@@ -347,3 +347,3 @@ it( "should populate only the tag name when no attribute are set, and no inner HTML is set", function() { | ||
expect( tag.toString() ).toBe( '<a></a>' ); | ||
expect( tag.toAnchorString() ).toBe( '<a></a>' ); | ||
} ); | ||
@@ -358,3 +358,3 @@ | ||
expect( tag.toString() ).toBe( '<a>My Site</a>' ); | ||
expect( tag.toAnchorString() ).toBe( '<a>My Site</a>' ); | ||
} ); | ||
@@ -369,3 +369,3 @@ | ||
expect( tag.toString() ).toBe( '<a href="http://path/to/site" rel="nofollow"></a>' ); | ||
expect( tag.toAnchorString() ).toBe( '<a href="http://path/to/site" rel="nofollow"></a>' ); | ||
} ); | ||
@@ -381,3 +381,3 @@ | ||
expect( tag.toString() ).toBe( '<a href="http://path/to/site" rel="nofollow">My Site</a>' ); | ||
expect( tag.toAnchorString() ).toBe( '<a href="http://path/to/site" rel="nofollow">My Site</a>' ); | ||
} ); | ||
@@ -394,3 +394,3 @@ | ||
expect( tag.toString() ).toBe( '<a class="test test2" href="http://path/to/site">My Site</a>' ); | ||
expect( tag.toAnchorString() ).toBe( '<a class="test test2" href="http://path/to/site">My Site</a>' ); | ||
} ); | ||
@@ -397,0 +397,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
290385
31
5710
258