-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Link style #1681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Link style #1681
Changes from all commits
b5cfd69
aa9fc00
4e4b8c8
62ef3d7
1019d7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -221,19 +221,25 @@ function texToSVG(_texString, _config, _callback) { | |
} | ||
|
||
var TAG_STYLES = { | ||
// would like to use baseline-shift but FF doesn't support it yet | ||
// would like to use baseline-shift for sub/sup but FF doesn't support it | ||
// so we need to use dy along with the uber hacky shift-back-to | ||
// baseline below | ||
sup: 'font-size:70%" dy="-0.6em', | ||
sub: 'font-size:70%" dy="0.3em', | ||
b: 'font-weight:bold', | ||
i: 'font-style:italic', | ||
a: '', | ||
a: 'cursor:pointer', | ||
span: '', | ||
br: '', | ||
em: 'font-style:italic;font-weight:bold' | ||
}; | ||
|
||
// sub/sup: extra tspan with zero-width space to get back to the right baseline | ||
var TAG_CLOSE = { | ||
sup: '<tspan dy="0.42em">​</tspan>', | ||
sub: '<tspan dy="-0.21em">​</tspan>' | ||
}; | ||
|
||
var PROTOCOLS = ['http:', 'https:', 'mailto:']; | ||
|
||
var STRIP_TAGS = new RegExp('</?(' + Object.keys(TAG_STYLES).join('|') + ')( [^>]*)?/?>', 'g'); | ||
|
@@ -254,6 +260,18 @@ var UNICODE_TO_ENTITY = Object.keys(stringMappings.unicodeToEntity).map(function | |
|
||
var NEWLINES = /(\r\n?|\n)/g; | ||
|
||
var SPLIT_TAGS = /(<[^<>]*>)/; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐎 |
||
|
||
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 | ||
var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (PSA: have to be careful with |
||
var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i; | ||
|
||
var COLORMATCH = /(^|;)\s*color:/; | ||
|
||
exports.plainText = function(_str) { | ||
// strip out our pseudo-html so we have a readable | ||
// version to put into text fields | ||
|
@@ -280,84 +298,93 @@ function encodeForHTML(_str) { | |
} | ||
|
||
function convertToSVG(_str) { | ||
_str = convertEntities(_str); | ||
|
||
// normalize behavior between IE and others wrt newlines and whitespace:pre | ||
// this combination makes IE barf https://github.com/plotly/plotly.js/issues/746 | ||
// Chrome and FF display \n, \r, or \r\n as a space in this mode. | ||
// I feel like at some point we turned these into <br> but currently we don't so | ||
// I'm just going to cement what we do now in Chrome and FF | ||
_str = _str.replace(NEWLINES, ' '); | ||
_str = convertEntities(_str) | ||
/* | ||
* Normalize behavior between IE and others wrt newlines and whitespace:pre | ||
* this combination makes IE barf https://github.com/plotly/plotly.js/issues/746 | ||
* Chrome and FF display \n, \r, or \r\n as a space in this mode. | ||
* I feel like at some point we turned these into <br> but currently we don't so | ||
* I'm just going to cement what we do now in Chrome and FF | ||
*/ | ||
.replace(NEWLINES, ' '); | ||
|
||
var result = _str | ||
.split(/(<[^<>]*>)/).map(function(d) { | ||
var match = d.match(/<(\/?)([^ >]*)\s*(.*)>/i), | ||
tag = match && match[2].toLowerCase(), | ||
style = TAG_STYLES[tag]; | ||
|
||
if(style !== undefined) { | ||
var close = match[1], | ||
extra = match[3], | ||
/** | ||
* extraStyle: any random extra css (that's supported by svg) | ||
* use this like <span style="font-family:Arial"> to change font in the middle | ||
* | ||
* at one point we supported <font family="..." size="..."> but as this isn't even | ||
* valid HTML anymore and we dropped it accidentally for many months, we will not | ||
* resurrect it. | ||
*/ | ||
extraStyle = extra.match(/^style\s*=\s*"([^"]+)"\s*/i); | ||
|
||
// anchor and br are the only ones that don't turn into a tspan | ||
.split(SPLIT_TAGS).map(function(d) { | ||
var match = d.match(ONE_TAG); | ||
var tag = match && match[2].toLowerCase(); | ||
var tagStyle = TAG_STYLES[tag]; | ||
|
||
if(tagStyle !== undefined) { | ||
var isClose = match[1]; | ||
if(isClose) return (tag === 'a' ? '</a>' : '</tspan>') + (TAG_CLOSE[tag] || ''); | ||
|
||
// break: later we'll turn these into newline <tspan>s | ||
// but we need to know about all the other tags first | ||
if(tag === 'br') return '<br>'; | ||
|
||
/** | ||
* extra includes href and any random extra css (that's supported by svg) | ||
* use this like <span style="font-family:Arial"> to change font in the middle | ||
* | ||
* at one point we supported <font family="..." size="..."> but as this isn't even | ||
* valid HTML anymore and we dropped it accidentally for many months, we will not | ||
* resurrect it. | ||
*/ | ||
var extra = match[4]; | ||
|
||
var out; | ||
|
||
// anchor is the only tag that doesn't turn into a tspan | ||
if(tag === 'a') { | ||
if(close) return '</a>'; | ||
else if(extra.substr(0, 4).toLowerCase() !== 'href') return '<a>'; | ||
else { | ||
// remove quotes, leading '=', replace '&' with '&' | ||
var href = extra.substr(4) | ||
.replace(/["']/g, '') | ||
.replace(/=/, ''); | ||
|
||
// check protocol | ||
var hrefMatch = extra && extra.match(HREFMATCH); | ||
var href = hrefMatch && (hrefMatch[3] || hrefMatch[4]); | ||
|
||
out = '<a'; | ||
|
||
if(href) { | ||
// check safe protocols | ||
var dummyAnchor = document.createElement('a'); | ||
dummyAnchor.href = href; | ||
if(PROTOCOLS.indexOf(dummyAnchor.protocol) === -1) return '<a>'; | ||
|
||
return '<a xlink:show="new" xlink:href="' + encodeForHTML(href) + '">'; | ||
if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) { | ||
out += ' xlink:show="new" xlink:href="' + encodeForHTML(href) + '"'; | ||
} | ||
} | ||
} | ||
else if(tag === 'br') return '<br>'; | ||
else if(close) { | ||
// closing tag | ||
|
||
// sub/sup: extra tspan with zero-width space to get back to the right baseline | ||
if(tag === 'sup') return '</tspan><tspan dy="0.42em">​</tspan>'; | ||
if(tag === 'sub') return '</tspan><tspan dy="-0.21em">​</tspan>'; | ||
else return '</tspan>'; | ||
} | ||
else { | ||
var tspanStart = '<tspan'; | ||
out = '<tspan'; | ||
|
||
if(tag === 'sup' || tag === 'sub') { | ||
// sub/sup: extra zero-width space, fixes problem if new line starts with sub/sup | ||
tspanStart = '​' + tspanStart; | ||
} | ||
|
||
if(extraStyle) { | ||
// most of the svg css users will care about is just like html, | ||
// but font color is different. Let our users ignore this. | ||
extraStyle = extraStyle[1].replace(/(^|;)\s*color:/, '$1 fill:'); | ||
style = encodeForHTML(extraStyle) + (style ? ';' + style : ''); | ||
out = '​' + out; | ||
} | ||
} | ||
|
||
return tspanStart + (style ? ' style="' + style + '"' : '') + '>'; | ||
// now add style, from both the tag name and any extra css | ||
// Most of the svg css that users will care about is just like html, | ||
// but font color is different (uses fill). Let our users ignore this. | ||
var cssMatch = extra && extra.match(STYLEMATCH); | ||
var css = cssMatch && (cssMatch[3] || cssMatch[4]); | ||
if(css) { | ||
css = encodeForHTML(css.replace(COLORMATCH, '$1 fill:')); | ||
if(tagStyle) css += ';' + tagStyle; | ||
} | ||
else if(tagStyle) css = tagStyle; | ||
|
||
if(css) return out + ' style="' + css + '">'; | ||
|
||
return out + '>'; | ||
} | ||
else { | ||
return exports.xml_entity_encode(d).replace(/</g, '<'); | ||
} | ||
}); | ||
|
||
// now deal with line breaks | ||
// TODO: this next section attempts to close and reopen tags that | ||
// span a line break. But | ||
// a) it only closes and reopens one tag, and | ||
// b) all tags are treated like equivalent tspans (even <a> which isn't a tspan even now!) | ||
// we should really do this in a type-aware way *before* converting to tspans. | ||
var indices = []; | ||
for(var index = result.indexOf('<br>'); index > 0; index = result.indexOf('<br>', index + 1)) { | ||
indices.push(index); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌