diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 0f1d538b6a4..10242e4a639 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -282,11 +282,36 @@ var SPLIT_TAGS = /(<[^<>]*>)/; var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i; -// Style and href: pull them out of either single or double quotes. -// Because we hack in other attributes with style (sub & sup), drop any trailing -// semicolon in user-supplied styles so we can consistently append the tag-dependent style +/* + * style and href: pull them out of either single or double quotes. Also + * - target: (_blank|_self|_parent|_top|framename) + * note that you can't use target to get a popup but if you use popup, + * a `framename` will be passed along as the name of the popup window. + * per the spec, cannot contain whitespace. + * for backward compatibility we default to '_blank' + * - popup: a custom one for us to enable popup (new window) links. String + * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550' + * note that at least in Chrome, you need to give at least one property + * in this string or the page will open in a new tab anyway. We follow this + * convention and will not make a popup if this string is empty. + * per the spec, cannot contain whitespace. + * + * Because we hack in other attributes with style (sub & sup), drop any trailing + * semicolon in user-supplied styles so we can consistently append the tag-dependent style + */ var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i; var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i; +var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i; +var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i; + +// dedicated matcher for these quoted regexes, that can return their results +// in two different places +function getQuotedMatch(_str, re) { + if(!_str) return null; + var match = _str.match(re); + return match && (match[3] || match[4]); +} + var COLORMATCH = /(^|;)\s*color:/; @@ -297,14 +322,14 @@ exports.plainText = function(_str) { }; function replaceFromMapObject(_str, list) { - var out = _str || ''; + if(!_str) return ''; for(var i = 0; i < list.length; i++) { var item = list[i]; - out = out.replace(item.regExp, item.sub); + _str = _str.replace(item.regExp, item.sub); } - return out; + return _str; } function convertEntities(_str) { @@ -354,8 +379,7 @@ function convertToSVG(_str) { // anchor is the only tag that doesn't turn into a tspan if(tag === 'a') { - var hrefMatch = extra && extra.match(HREFMATCH); - var href = hrefMatch && (hrefMatch[3] || hrefMatch[4]); + var href = getQuotedMatch(extra, HREFMATCH); out = 'link'); + assertAnchorLink(node, 'x', spec.target, spec.show, spec.target); + }); + }); + + it('attaches onclick if popup is specified', function() { + var node = mockTextSVGElement('link'); + assertAnchorLink(node, 'x', 'fred', 'new'); + assertAnchorAttrs(node, {onclick: 'window.open(\'x\',\'fred\',\'width=500,height=400\');return false;'}); + }); + it('keeps query parameters in href', function() { var textCases = [ 'abc.com?shared-key', @@ -171,9 +202,9 @@ describe('svg+text utils', function() { textCases.forEach(function(textCase) { var node = mockTextSVGElement(textCase); - assertAnchorAttrs(node); - expect(node.text()).toEqual('abc.com?shared-key'); - assertAnchorLink(node, 'https://abc.com/myFeature.jsp?name=abc&pwd=def'); + assertAnchorAttrs(node, {}, textCase); + expect(node.text()).toEqual('abc.com?shared-key', textCase); + assertAnchorLink(node, 'https://abc.com/myFeature.jsp?name=abc&pwd=def', undefined, undefined, textCase); }); });