@@ -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,16 @@ 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
+ var QUOTEDATTR = '\\s*=\\s*("([^"]*)"|\'([^\']*)\')' ;
268
+ var STYLEMATCH = new RegExp ( '(^|[\\s"\'])style' + QUOTEDATTR , 'i' ) ;
269
+ var HREFMATCH = new RegExp ( '(^|[\\s"\'])href' + QUOTEDATTR , 'i' ) ;
270
+
271
+ var COLORMATCH = / ( ^ | ; ) \s * c o l o r : / ;
272
+
257
273
exports . plainText = function ( _str ) {
258
274
// strip out our pseudo-html so we have a readable
259
275
// version to put into text fields
@@ -280,84 +296,86 @@ function encodeForHTML(_str) {
280
296
}
281
297
282
298
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 , ' ' ) ;
299
+ _str = convertEntities ( _str )
300
+ /*
301
+ * Normalize behavior between IE and others wrt newlines and whitespace:pre
302
+ * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
303
+ * Chrome and FF display \n, \r, or \r\n as a space in this mode.
304
+ * I feel like at some point we turned these into <br> but currently we don't so
305
+ * I'm just going to cement what we do now in Chrome and FF
306
+ */
307
+ . replace ( NEWLINES , ' ' ) ;
291
308
292
309
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
310
+ . split ( SPLIT_TAGS ) . map ( function ( d ) {
311
+ var match = d . match ( ONE_TAG ) ;
312
+ var tag = match && match [ 2 ] . toLowerCase ( ) ;
313
+ var tagStyle = TAG_STYLES [ tag ] ;
314
+
315
+ if ( tagStyle !== undefined ) {
316
+ var isClose = match [ 1 ] ;
317
+ if ( isClose ) return ( tag === 'a' ? '</a>' : '</tspan>' ) + ( TAG_CLOSE [ tag ] || '' ) ;
318
+
319
+ // break: later we'll turn these into newline <tspan>s
320
+ // but we need to know about all the other tags first
321
+ if ( tag === 'br' ) return '<br>' ;
322
+
323
+ /**
324
+ * extra includes href and any random extra css (that's supported by svg)
325
+ * use this like <span style="font-family:Arial"> to change font in the middle
326
+ *
327
+ * at one point we supported <font family="..." size="..."> but as this isn't even
328
+ * valid HTML anymore and we dropped it accidentally for many months, we will not
329
+ * resurrect it.
330
+ */
331
+ var extra = match [ 4 ] ;
332
+
333
+ var out ;
334
+
335
+ // anchor is the only tag that doesn't turn into a tspan
312
336
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
337
+ var hrefMatch = extra && extra . match ( HREFMATCH ) ;
338
+ var href = hrefMatch && ( hrefMatch [ 3 ] || hrefMatch [ 4 ] ) ;
339
+
340
+ out = '<a' ;
341
+
342
+ if ( href ) {
343
+ // check safe protocols
322
344
var dummyAnchor = document . createElement ( 'a' ) ;
323
345
dummyAnchor . href = href ;
324
- if ( PROTOCOLS . indexOf ( dummyAnchor . protocol ) === - 1 ) return '<a>' ;
325
-
326
- return '<a xlink:show="new" xlink:href="' + encodeForHTML ( href ) + '">' ;
346
+ if ( PROTOCOLS . indexOf ( dummyAnchor . protocol ) !== - 1 ) {
347
+ out += ' xlink:show="new" xlink:href="' + encodeForHTML ( href ) + '"' ;
348
+ }
327
349
}
328
350
}
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
351
else {
339
- var tspanStart = '<tspan' ;
352
+ out = '<tspan' ;
340
353
341
354
if ( tag === 'sup' || tag === 'sub' ) {
342
355
// sub/sup: extra zero-width space, fixes problem if new line starts with sub/sup
343
- tspanStart = '​' + tspanStart ;
356
+ out = '​' + out ;
344
357
}
358
+ }
345
359
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 : '' ) ;
351
- }
360
+ // now add style, from both the tag name and any extra css
361
+ // Most of the svg css that users will care about is just like html,
362
+ // but font color is different (uses fill) . Let our users ignore this.
363
+ var cssMatch = extra && extra . match ( STYLEMATCH ) ;
364
+ var css = cssMatch && ( cssMatch [ 3 ] || cssMatch [ 4 ] ) ;
365
+ if ( css ) css = encodeForHTML ( css . replace ( COLORMATCH , '$1 fill:' ) ) ;
352
366
353
- return tspanStart + ( style ? ' style="' + style + '"' : '' ) + '>' ;
354
- }
367
+ if ( tagStyle ) css = tagStyle + ';' + ( css || '' ) ;
368
+
369
+ if ( css ) return out + ' style="' + css + '">' ;
370
+
371
+ return out + '>' ;
355
372
}
356
373
else {
357
374
return exports . xml_entity_encode ( d ) . replace ( / < / g, '<' ) ;
358
375
}
359
376
} ) ;
360
377
378
+ // now deal with line breaks
361
379
var indices = [ ] ;
362
380
for ( var index = result . indexOf ( '<br>' ) ; index > 0 ; index = result . indexOf ( '<br>' , index + 1 ) ) {
363
381
indices . push ( index ) ;
0 commit comments