@@ -221,19 +221,25 @@ function texToSVG(_texString, _config, _callback) {
221
221
}
222
222
223
223
var TAG_STYLES = {
224
- // would like to use baseline-shift but FF doesn't support it yet
224
+ // would like to use baseline-shift for sub/sup but FF doesn't support it
225
225
// so we need to use dy along with the uber hacky shift-back-to
226
226
// baseline below
227
227
sup : 'font-size:70%" dy="-0.6em' ,
228
228
sub : 'font-size:70%" dy="0.3em' ,
229
229
b : 'font-weight:bold' ,
230
230
i : 'font-style:italic' ,
231
- a : '' ,
231
+ a : 'cursor:pointer ' ,
232
232
span : '' ,
233
233
br : '' ,
234
234
em : 'font-style:italic;font-weight:bold'
235
235
} ;
236
236
237
+ // sub/sup: extra tspan with zero-width space to get back to the right baseline
238
+ var TAG_CLOSE = {
239
+ sup : '<tspan dy="0.42em">​</tspan>' ,
240
+ sub : '<tspan dy="-0.21em">​</tspan>'
241
+ } ;
242
+
237
243
var PROTOCOLS = [ 'http:' , 'https:' , 'mailto:' ] ;
238
244
239
245
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
254
260
255
261
var NEWLINES = / ( \r \n ? | \n ) / g;
256
262
263
+ var SPLIT_TAGS = / ( < [ ^ < > ] * > ) / ;
264
+
265
+ var ONE_TAG = / < ( \/ ? ) ( [ ^ > ] * ) ( \s + ( .* ) ) ? > / i;
266
+
267
+ // Style and href: pull them out of either single or double quotes.
268
+ // Because we hack in other attributes with style (sub & sup), drop any trailing
269
+ // semicolon in user-supplied styles so we can consistently append the tag-dependent style
270
+ var STYLEMATCH = / ( ^ | [ \s " ' ] ) s t y l e \s * = \s * ( " ( [ ^ " ] * ) ; ? " | ' ( [ ^ ' ] * ) ; ? ' ) / i;
271
+ var HREFMATCH = / ( ^ | [ \s " ' ] ) h r e f \s * = \s * ( " ( [ ^ " ] * ) " | ' ( [ ^ ' ] * ) ' ) / i;
272
+
273
+ var COLORMATCH = / ( ^ | ; ) \s * c o l o r : / ;
274
+
257
275
exports . plainText = function ( _str ) {
258
276
// strip out our pseudo-html so we have a readable
259
277
// version to put into text fields
@@ -280,84 +298,93 @@ function encodeForHTML(_str) {
280
298
}
281
299
282
300
function convertToSVG ( _str ) {
283
- _str = convertEntities ( _str ) ;
284
-
285
- // normalize behavior between IE and others wrt newlines and whitespace:pre
286
- // this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
287
- // Chrome and FF display \n, \r, or \r\n as a space in this mode.
288
- // I feel like at some point we turned these into <br> but currently we don't so
289
- // I'm just going to cement what we do now in Chrome and FF
290
- _str = _str . replace ( NEWLINES , ' ' ) ;
301
+ _str = convertEntities ( _str )
302
+ /*
303
+ * Normalize behavior between IE and others wrt newlines and whitespace:pre
304
+ * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
305
+ * Chrome and FF display \n, \r, or \r\n as a space in this mode.
306
+ * I feel like at some point we turned these into <br> but currently we don't so
307
+ * I'm just going to cement what we do now in Chrome and FF
308
+ */
309
+ . replace ( NEWLINES , ' ' ) ;
291
310
292
311
var result = _str
293
- . split ( / ( < [ ^ < > ] * > ) / ) . map ( function ( d ) {
294
- var match = d . match ( / < ( \/ ? ) ( [ ^ > ] * ) \s * ( .* ) > / i) ,
295
- tag = match && match [ 2 ] . toLowerCase ( ) ,
296
- style = TAG_STYLES [ tag ] ;
297
-
298
- if ( style !== undefined ) {
299
- var close = match [ 1 ] ,
300
- extra = match [ 3 ] ,
301
- /**
302
- * extraStyle: any random extra css (that's supported by svg)
303
- * use this like <span style="font-family:Arial"> to change font in the middle
304
- *
305
- * at one point we supported <font family="..." size="..."> but as this isn't even
306
- * valid HTML anymore and we dropped it accidentally for many months, we will not
307
- * resurrect it.
308
- */
309
- extraStyle = extra . match ( / ^ s t y l e \s * = \s * " ( [ ^ " ] + ) " \s * / i) ;
310
-
311
- // anchor and br are the only ones that don't turn into a tspan
312
+ . split ( SPLIT_TAGS ) . map ( function ( d ) {
313
+ var match = d . match ( ONE_TAG ) ;
314
+ var tag = match && match [ 2 ] . toLowerCase ( ) ;
315
+ var tagStyle = TAG_STYLES [ tag ] ;
316
+
317
+ if ( tagStyle !== undefined ) {
318
+ var isClose = match [ 1 ] ;
319
+ if ( isClose ) return ( tag === 'a' ? '</a>' : '</tspan>' ) + ( TAG_CLOSE [ tag ] || '' ) ;
320
+
321
+ // break: later we'll turn these into newline <tspan>s
322
+ // but we need to know about all the other tags first
323
+ if ( tag === 'br' ) return '<br>' ;
324
+
325
+ /**
326
+ * extra includes href and any random extra css (that's supported by svg)
327
+ * use this like <span style="font-family:Arial"> to change font in the middle
328
+ *
329
+ * at one point we supported <font family="..." size="..."> but as this isn't even
330
+ * valid HTML anymore and we dropped it accidentally for many months, we will not
331
+ * resurrect it.
332
+ */
333
+ var extra = match [ 4 ] ;
334
+
335
+ var out ;
336
+
337
+ // anchor is the only tag that doesn't turn into a tspan
312
338
if ( tag === 'a' ) {
313
- if ( close ) return '</a>' ;
314
- else if ( extra . substr ( 0 , 4 ) . toLowerCase ( ) !== 'href' ) return '<a>' ;
315
- else {
316
- // remove quotes, leading '=', replace '&' with '&'
317
- var href = extra . substr ( 4 )
318
- . replace ( / [ " ' ] / g, '' )
319
- . replace ( / = / , '' ) ;
320
-
321
- // check protocol
339
+ var hrefMatch = extra && extra . match ( HREFMATCH ) ;
340
+ var href = hrefMatch && ( hrefMatch [ 3 ] || hrefMatch [ 4 ] ) ;
341
+
342
+ out = '<a' ;
343
+
344
+ if ( href ) {
345
+ // check safe protocols
322
346
var dummyAnchor = document . createElement ( 'a' ) ;
323
347
dummyAnchor . href = href ;
324
- if ( PROTOCOLS . indexOf ( dummyAnchor . protocol ) === - 1 ) return '<a>' ;
325
-
326
- return '<a xlink:show="new" xlink:href="' + encodeForHTML ( href ) + '">' ;
348
+ if ( PROTOCOLS . indexOf ( dummyAnchor . protocol ) !== - 1 ) {
349
+ out += ' xlink:show="new" xlink:href="' + encodeForHTML ( href ) + '"' ;
350
+ }
327
351
}
328
352
}
329
- else if ( tag === 'br' ) return '<br>' ;
330
- else if ( close ) {
331
- // closing tag
332
-
333
- // sub/sup: extra tspan with zero-width space to get back to the right baseline
334
- if ( tag === 'sup' ) return '</tspan><tspan dy="0.42em">​</tspan>' ;
335
- if ( tag === 'sub' ) return '</tspan><tspan dy="-0.21em">​</tspan>' ;
336
- else return '</tspan>' ;
337
- }
338
353
else {
339
- var tspanStart = '<tspan' ;
354
+ out = '<tspan' ;
340
355
341
356
if ( tag === 'sup' || tag === 'sub' ) {
342
357
// sub/sup: extra zero-width space, fixes problem if new line starts with sub/sup
343
- tspanStart = '​' + tspanStart ;
344
- }
345
-
346
- if ( extraStyle ) {
347
- // most of the svg css users will care about is just like html,
348
- // but font color is different. Let our users ignore this.
349
- extraStyle = extraStyle [ 1 ] . replace ( / ( ^ | ; ) \s * c o l o r : / , '$1 fill:' ) ;
350
- style = encodeForHTML ( extraStyle ) + ( style ? ';' + style : '' ) ;
358
+ out = '​' + out ;
351
359
}
360
+ }
352
361
353
- return tspanStart + ( style ? ' style="' + style + '"' : '' ) + '>' ;
362
+ // now add style, from both the tag name and any extra css
363
+ // Most of the svg css that users will care about is just like html,
364
+ // but font color is different (uses fill). Let our users ignore this.
365
+ var cssMatch = extra && extra . match ( STYLEMATCH ) ;
366
+ var css = cssMatch && ( cssMatch [ 3 ] || cssMatch [ 4 ] ) ;
367
+ if ( css ) {
368
+ css = encodeForHTML ( css . replace ( COLORMATCH , '$1 fill:' ) ) ;
369
+ if ( tagStyle ) css += ';' + tagStyle ;
354
370
}
371
+ else if ( tagStyle ) css = tagStyle ;
372
+
373
+ if ( css ) return out + ' style="' + css + '">' ;
374
+
375
+ return out + '>' ;
355
376
}
356
377
else {
357
378
return exports . xml_entity_encode ( d ) . replace ( / < / g, '<' ) ;
358
379
}
359
380
} ) ;
360
381
382
+ // now deal with line breaks
383
+ // TODO: this next section attempts to close and reopen tags that
384
+ // span a line break. But
385
+ // a) it only closes and reopens one tag, and
386
+ // b) all tags are treated like equivalent tspans (even <a> which isn't a tspan even now!)
387
+ // we should really do this in a type-aware way *before* converting to tspans.
361
388
var indices = [ ] ;
362
389
for ( var index = result . indexOf ( '<br>' ) ; index > 0 ; index = result . indexOf ( '<br>' , index + 1 ) ) {
363
390
indices . push ( index ) ;
0 commit comments