mermaid
Version:
Markdownish syntax for generating flowcharts, sequence diagrams and gantt charts.
421 lines (406 loc) • 57.9 kB
HTML
<!DOCTYPE html>
<html>
<head>
<title>textwrap.js</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" media="all" href="../doc-style.css" />
<script src="../doc-filelist.js"></script>
<script>
var relativeDir = "../", thisFile = "Users/knut/Documents/source/mermaid/src/textwrap.js", defaultSidebar = true;
</script>
<script src="../doc-script.js"></script>
<script src="../mermaid.js"></script>
<link rel="stylesheet" href="../mermaid.css" />
</head>
<body>
<div id="sidebar_wrapper">
<div id="sidebar_switch">
<span class="tree">Files</span>
<span class="headings">Headings</span>
</div>
<div id="tree"></div>
<div id="headings">
<div class="heading h1">
<a href="#textwrap.js">textwrap.js</a>
</div>
</div>
</div>
<div id="sidebar-toggle"></div>
<div id="container"><div class="background highlight"></div>
<table cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td class="docs">
<div class="pilwrap" id="textwrap.js">
<h1>
<a href="#textwrap.js" name="textwrap.js" class="pilcrow">¶</a>textwrap.js
</h1>
</div>
</td>
<td class="code highlight"></td>
</tr>
<tr>
<td class="docs">
<div class="pilwrap">
<a class="pilcrow" href="#section-1" id="section-1">¶</a>
</div>
<p>/**</p>
<ul>
<li>Created by knut on 2015-07-21.
<em>/
/</em></li>
</ul>
<p>D3 Text Wrap
By Vijith Assar
<a href="http://www.vijithassar.com">http://www.vijithassar.com</a>
<a href="http://www.github.com/vijithassar">http://www.github.com/vijithassar</a>
@vijithassar</p>
<p>Detailed instructions at <a href="http://www.github.com/vijithassar/d3textwrap">http://www.github.com/vijithassar/d3textwrap</a></p>
<p>*/</p>
<p>(function() {</p>
<p> // set this variable to a string value to always force a particular
// wrap method for development purposes, for example to check tspan
// rendering using a foreignobject-enabled browser. set to 'tspan' to
// use tspans and 'foreignobject' to use foreignobject
var force_wrap_method = false; // by default no wrap method is forced
force_wrap_method = 'tspans'; // uncomment this statement to force tspans
// force_wrap_method = 'foreignobjects'; // uncomment this statement to force foreignobjects</p>
<p> // exit immediately if something in this location
// has already been defined; the plugin will defer to whatever
// else you're doing in your code
if(d3.selection.prototype.textwrap) {
return false;
}</p>
<p> // double check the force_wrap_method flag
// and reset if someone screwed up the above
// settings
if(typeof force_wrap_method == 'undefined') {
var force_wrap_method = false;
}</p>
<p> // create the plugin method twice, both for regular use
// and again for use inside the enter() selection
d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function(bounds, padding) {</p>
<div class="highlight"><pre><code> <span class="c1">// default value of padding is zero if it's undefined</span>
<span class="kd">var</span> <span class="nx">padding</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">padding</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// save callee into a variable so we can continue to refer to it</span>
<span class="c1">// as the function scope changes</span>
<span class="kd">var</span> <span class="nx">selection</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="c1">// create a variable to store desired return values in</span>
<span class="kd">var</span> <span class="nx">return_value</span><span class="p">;</span>
<span class="c1">// extract wrap boundaries from any d3-selected rect and return them</span>
<span class="c1">// in a format that matches the simpler object argument option</span>
<span class="kd">var</span> <span class="nx">extract_bounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">bounds</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// discard the nested array wrappers added by d3</span>
<span class="kd">var</span> <span class="nx">bounding_rect</span> <span class="o">=</span> <span class="nx">bounds</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span>
<span class="c1">// sanitize the svg element name so we can test against it</span>
<span class="kd">var</span> <span class="nx">element_type</span> <span class="o">=</span> <span class="nx">bounding_rect</span><span class="p">.</span><span class="nx">tagName</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="c1">// if it's not a rect, exit</span>
<span class="k">if</span><span class="p">(</span><span class="nx">element_type</span> <span class="o">!==</span> <span class="s1">'rect'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="c1">// if it's a rect, proceed to extracting the position attributes</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">bounds_extracted</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">bounds_extracted</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">bounding_rect</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'x'</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">bounds_extracted</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">bounding_rect</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'y'</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">bounds_extracted</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">bounding_rect</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'width'</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">bounds_extracted</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">bounding_rect</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'height'</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// also pass along the getter function</span>
<span class="nx">bounds_extracted</span><span class="p">.</span><span class="nx">attr</span> <span class="o">=</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">attr</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">bounds_extracted</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// double check the input argument for the wrapping</span>
<span class="c1">// boundaries to make sure it actually contains all</span>
<span class="c1">// the information we'll need in order to wrap successfully</span>
<span class="kd">var</span> <span class="nx">verify_bounds</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">bounds</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// quickly add a simple getter method so you can use either</span>
<span class="c1">// bounds.x or bounds.attr('x') as your notation,</span>
<span class="c1">// the latter being a common convention among D3</span>
<span class="c1">// developers</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">bounds</span><span class="p">.</span><span class="nx">attr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">bounds</span><span class="p">.</span><span class="nx">attr</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">property</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="nx">property</span><span class="p">])</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">[</span><span class="nx">property</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// if it's an associative array, make sure it has all the</span>
<span class="c1">// necessary properties represented directly</span>
<span class="k">if</span><span class="p">(</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nx">bounds</span> <span class="o">==</span> <span class="s1">'object'</span><span class="p">)</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">x</span> <span class="o">!==</span> <span class="s1">'undefined'</span><span class="p">)</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">y</span> <span class="o">!==</span> <span class="s1">'undefined'</span><span class="p">)</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span> <span class="o">!==</span> <span class="s1">'undefined'</span><span class="p">)</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">height</span> <span class="o">!==</span> <span class="s1">'undefined'</span><span class="p">)</span>
<span class="c1">// if that's the case, then the bounds are fine</span>
<span class="p">)</span> <span class="p">{</span>
<span class="c1">// return the lightly modified bounds</span>
<span class="k">return</span> <span class="nx">bounds</span><span class="p">;</span>
<span class="c1">// if it's a numerically indexed array, assume it's a</span>
<span class="c1">// d3-selected rect and try to extract the positions</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span>
<span class="c1">// first try to make sure it's an array using Array.isArray</span>
<span class="p">(</span>
<span class="p">(</span><span class="k">typeof</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span> <span class="o">==</span> <span class="s1">'function'</span><span class="p">)</span>
<span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">bounds</span><span class="p">))</span>
<span class="p">)</span> <span class="o">||</span>
<span class="c1">// but since Array.isArray isn't always supported, fall</span>
<span class="c1">// back to casting to the object to string when it's not</span>
<span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">bounds</span><span class="p">)</span> <span class="o">===</span> <span class="s1">'[object Array]'</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
<span class="c1">// once you're sure it's an array, extract the boundaries</span>
<span class="c1">// from the rect</span>
<span class="kd">var</span> <span class="nx">extracted_bounds</span> <span class="o">=</span> <span class="nx">extract_bounds</span><span class="p">(</span><span class="nx">bounds</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">extracted_bounds</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// but if the bounds are neither an object nor a numerical</span>
<span class="c1">// array, then the bounds argument is invalid and you'll</span>
<span class="c1">// need to fix it</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">apply_padding</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">bounds</span><span class="p">,</span> <span class="nx">padding</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">padded_bounds</span> <span class="o">=</span> <span class="nx">bounds</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="nx">padding</span> <span class="o">!==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span> <span class="o">+</span> <span class="nx">padding</span><span class="p">;</span>
<span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">y</span><span class="p">)</span> <span class="o">+</span> <span class="nx">padding</span><span class="p">;</span>
<span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">width</span> <span class="o">-=</span> <span class="nx">padding</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="nx">padded_bounds</span><span class="p">.</span><span class="nx">height</span> <span class="o">-=</span> <span class="nx">padding</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">padded_bounds</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// verify bounds</span>
<span class="kd">var</span> <span class="nx">verified_bounds</span> <span class="o">=</span> <span class="nx">verify_bounds</span><span class="p">(</span><span class="nx">bounds</span><span class="p">);</span>
<span class="c1">// modify bounds if a padding value is provided</span>
<span class="k">if</span><span class="p">(</span><span class="nx">padding</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">verified_bounds</span> <span class="o">=</span> <span class="nx">apply_padding</span><span class="p">(</span><span class="nx">verified_bounds</span><span class="p">,</span> <span class="nx">padding</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// check that we have the necessary conditions for this function to operate properly</span>
<span class="k">if</span><span class="p">(</span>
<span class="c1">// selection it's operating on cannot be not empty</span>
<span class="p">(</span><span class="nx">selection</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">||</span>
<span class="c1">// d3 must be available</span>
<span class="p">(</span><span class="o">!</span><span class="nx">d3</span><span class="p">)</span> <span class="o">||</span>
<span class="c1">// desired wrapping bounds must be provided as an input argument</span>
<span class="p">(</span><span class="o">!</span><span class="nx">bounds</span><span class="p">)</span> <span class="o">||</span>
<span class="c1">// input bounds must validate</span>
<span class="p">(</span><span class="o">!</span><span class="nx">verified_bounds</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
<span class="c1">// try to return the calling selection if possible</span>
<span class="c1">// so as not to interfere with methods downstream in the</span>
<span class="c1">// chain</span>
<span class="k">if</span><span class="p">(</span><span class="nx">selection</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">selection</span><span class="p">;</span>
<span class="c1">// if all else fails, just return false. if you hit this point then you're</span>
<span class="c1">// almost certainly trying to call the textwrap() method on something that</span>
<span class="c1">// doesn't make sense!</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// if we've validated everything then we can finally proceed</span>
<span class="c1">// to the meat of this operation</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// reassign the verified bounds as the set we want</span>
<span class="c1">// to work with from here on; this ensures that we're</span>
<span class="c1">// using the same data structure for our bounds regardless</span>
<span class="c1">// of whether the input argument was a simple object or</span>
<span class="c1">// a d3 selection</span>
<span class="nx">bounds</span> <span class="o">=</span> <span class="nx">verified_bounds</span><span class="p">;</span>
<span class="c1">// wrap using html and foreignObjects if they are supported</span>
<span class="kd">var</span> <span class="nx">wrap_with_foreignobjects</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// establish variables to quickly reference target nodes later</span>
<span class="kd">var</span> <span class="nx">parent</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">item</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">parentNode</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">text_node</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="s1">'text'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">styled_line_height</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">style</span><span class="p">(</span><span class="s1">'line-height'</span><span class="p">);</span>
<span class="c1">// extract our desired content from the single text element</span>
<span class="kd">var</span> <span class="nx">text_to_wrap</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">text</span><span class="p">();</span>
<span class="c1">// remove the text node and replace with a foreign object</span>
<span class="nx">text_node</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">foreign_object</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'foreignObject'</span><span class="p">);</span>
<span class="c1">// add foreign object and set dimensions, position, etc</span>
<span class="nx">foreign_object</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="nx">requiredFeatures</span><span class="p">,</span> <span class="nx">http</span><span class="o">:</span><span class="c1">//www.w3.org/TR/SVG11/feature#Extensibility)</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'x'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">y</span><span class="p">)</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'width'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span><span class="p">)</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'height'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
<span class="c1">// insert an HTML div</span>
<span class="kd">var</span> <span class="nx">wrap_div</span> <span class="o">=</span> <span class="nx">foreign_object</span>
<span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'xhtml:div'</span><span class="p">)</span>
<span class="c1">// this class is currently hardcoded</span>
<span class="c1">// probably not necessary but easy to</span>
<span class="c1">// override using .classed() and for now</span>
<span class="c1">// it's nice to avoid a litany of input</span>
<span class="c1">// arguments</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="s1">'wrapped'</span><span class="p">);</span>
<span class="c1">// set div to same dimensions as foreign object</span>
<span class="nx">wrap_div</span>
<span class="p">.</span><span class="nx">style</span><span class="p">(</span><span class="s1">'height'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">height</span><span class="p">)</span>
<span class="p">.</span><span class="nx">style</span><span class="p">(</span><span class="s1">'width'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span><span class="p">)</span>
<span class="c1">// insert text content</span>
<span class="p">.</span><span class="nx">html</span><span class="p">(</span><span class="nx">text_to_wrap</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nx">styled_line_height</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">wrap_div</span><span class="p">.</span><span class="nx">style</span><span class="p">(</span><span class="s1">'line-height'</span><span class="p">,</span> <span class="nx">styled_line_height</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">return_value</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="s1">'foreignObject'</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// wrap with tspans if foreignObject is undefined</span>
<span class="kd">var</span> <span class="nx">wrap_with_tspans</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// operate on the first text item in the selection</span>
<span class="kd">var</span> <span class="nx">text_node</span> <span class="o">=</span> <span class="nx">item</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">parent</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">text_node_selected</span> <span class="o">=</span> <span class="nx">d3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">text_node</span><span class="p">);</span>
<span class="c1">// measure initial size of the text node as rendered</span>
<span class="kd">var</span> <span class="nx">text_node_height</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">getBBox</span><span class="p">().</span><span class="nx">height</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">text_node_width</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">getBBox</span><span class="p">().</span><span class="nx">width</span><span class="p">;</span>
<span class="c1">// figure out the line height, either from rendered height</span>
<span class="c1">// of the font or attached styling</span>
<span class="kd">var</span> <span class="nx">line_height</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">rendered_line_height</span> <span class="o">=</span> <span class="nx">text_node_height</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">styled_line_height</span> <span class="o">=</span> <span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">style</span><span class="p">(</span><span class="s1">'line-height'</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span>
<span class="p">(</span><span class="nx">styled_line_height</span><span class="p">)</span>
<span class="p">(</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">styled_line_height</span><span class="p">))</span>
<span class="p">)</span> <span class="p">{</span>
<span class="nx">line_height</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">styled_line_height</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'px'</span><span class="p">,</span> <span class="s1">''</span><span class="p">));</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">line_height</span> <span class="o">=</span> <span class="nx">rendered_line_height</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// only fire the rest of this if the text content</span>
<span class="c1">// overflows the desired dimensions</span>
<span class="k">if</span><span class="p">(</span><span class="nx">text_node_width</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// store whatever is inside the text node</span>
<span class="c1">// in a variable and then zero out the</span>
<span class="c1">// initial content; we'll reinsert in a moment</span>
<span class="c1">// using tspan elements.</span>
<span class="kd">var</span> <span class="nx">text_to_wrap</span> <span class="o">=</span> <span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">();</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nx">text_to_wrap</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// keep track of whether we are splitting by spaces</span>
<span class="c1">// so we know whether to reinsert those spaces later</span>
<span class="kd">var</span> <span class="nx">break_delimiter</span><span class="p">;</span>
<span class="c1">// split at spaces to create an array of individual words</span>
<span class="kd">var</span> <span class="nx">text_to_wrap_array</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="nx">text_to_wrap</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">' '</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">break_delimiter</span> <span class="o">=</span> <span class="s1">' '</span><span class="p">;</span>
<span class="nx">text_to_wrap_array</span> <span class="o">=</span> <span class="nx">text_to_wrap</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">' '</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// if there are no spaces, figure out the split</span>
<span class="c1">// points by comparing rendered text width against</span>
<span class="c1">// bounds and translating that into character position</span>
<span class="c1">// cuts</span>
<span class="nx">break_delimiter</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">string_length</span> <span class="o">=</span> <span class="nx">text_to_wrap</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">number_of_substrings</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">ceil</span><span class="p">(</span><span class="nx">text_node_width</span> <span class="o">/</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">splice_interval</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">string_length</span> <span class="o">/</span> <span class="nx">number_of_substrings</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span>
<span class="o">!</span><span class="p">(</span><span class="nx">splice_interval</span> <span class="o">*</span> <span class="nx">number_of_substrings</span> <span class="o">=</span> <span class="nx">string_length</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
<span class="nx">number_of_substrings</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">text_to_wrap_array</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">var</span> <span class="nx">substring</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">start_position</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="nx">number_of_substrings</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">start_position</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">*</span> <span class="nx">splice_interval</span><span class="p">;</span>
<span class="nx">substring</span> <span class="o">=</span> <span class="nx">text_to_wrap</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="nx">start_position</span><span class="p">,</span> <span class="nx">splice_interval</span><span class="p">);</span>
<span class="nx">text_to_wrap_array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">substring</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// new array where we'll store the words re-assembled into</span>
<span class="c1">// substrings that have been tested against the desired</span>
<span class="c1">// maximum wrapping width</span>
<span class="kd">var</span> <span class="nx">substrings</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// computed text length is arguably incorrectly reported for</span>
<span class="c1">// all tspans after the first one, in that they will include</span>
<span class="c1">// the width of previous separate tspans. to compensate we need</span>
<span class="c1">// to manually track the computed text length of all those</span>
<span class="c1">// previous tspans and substrings, and then use that to offset</span>
<span class="c1">// the miscalculation. this then gives us the actual correct</span>
<span class="c1">// position we want to use in rendering the text in the SVG.</span>
<span class="kd">var</span> <span class="nx">total_offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// object for storing the results of text length computations later</span>
<span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="p">{};</span>
<span class="c1">// loop through the words and test the computed text length</span>
<span class="c1">// of the string against the maximum desired wrapping width</span>
<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="nx">text_to_wrap_array</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">word</span> <span class="o">=</span> <span class="nx">text_to_wrap_array</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">previous_string</span> <span class="o">=</span> <span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">previous_width</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">getComputedTextLength</span><span class="p">();</span>
<span class="c1">// initialize the current word as the first word</span>
<span class="c1">// or append to the previous string if one exists</span>
<span class="kd">var</span> <span class="nx">new_string</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="nx">previous_string</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">new_string</span> <span class="o">=</span> <span class="nx">previous_string</span> <span class="o">+</span> <span class="nx">break_delimiter</span> <span class="o">+</span> <span class="nx">word</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">new_string</span> <span class="o">=</span> <span class="nx">word</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// add the newest substring back to the text node and</span>
<span class="c1">// measure the length</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="nx">new_string</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">new_width</span> <span class="o">=</span> <span class="nx">text_node</span><span class="p">.</span><span class="nx">getComputedTextLength</span><span class="p">();</span>
<span class="c1">// adjust the length by the offset we've tracked</span>
<span class="c1">// due to the misreported length discussed above</span>
<span class="kd">var</span> <span class="nx">test_width</span> <span class="o">=</span> <span class="nx">new_width</span> <span class="o">-</span> <span class="nx">total_offset</span><span class="p">;</span>
<span class="c1">// if our latest version of the string is too</span>
<span class="c1">// big for the bounds, use the previous</span>
<span class="c1">// version of the string (without the newest word</span>
<span class="c1">// added) and use the latest word to restart the</span>
<span class="c1">// process with a new tspan</span>
<span class="k">if</span><span class="p">(</span><span class="nx">new_width</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">width</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span>
<span class="p">(</span><span class="nx">previous_string</span><span class="p">)</span>
<span class="p">(</span><span class="nx">previous_string</span> <span class="o">!==</span> <span class="s1">''</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
<span class="nx">total_offset</span> <span class="o">=</span> <span class="nx">total_offset</span> <span class="o">+</span> <span class="nx">previous_width</span><span class="p">;</span>
<span class="nx">temp</span> <span class="o">=</span> <span class="p">{</span><span class="nx">string</span><span class="o">:</span> <span class="nx">previous_string</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">previous_width</span><span class="p">,</span> <span class="nx">offset</span><span class="o">:</span> <span class="nx">total_offset</span><span class="p">};</span>
<span class="nx">substrings</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">temp</span><span class="p">);</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="nx">word</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// if we're up to the last word in the array,</span>
<span class="c1">// get the computed length as is without</span>
<span class="c1">// appending anything further to it</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">i</span> <span class="o">==</span> <span class="nx">text_to_wrap_array</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">final_string</span> <span class="o">=</span> <span class="nx">new_string</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span>
<span class="p">(</span><span class="nx">final_string</span><span class="p">)</span>
<span class="p">(</span><span class="nx">final_string</span> <span class="o">!==</span> <span class="s1">''</span><span class="p">)</span>
<span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">((</span><span class="nx">new_width</span> <span class="o">-</span> <span class="nx">total_offset</span><span class="p">)</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span><span class="nx">new_width</span> <span class="o">=</span> <span class="nx">new_width</span> <span class="o">-</span> <span class="nx">total_offset</span><span class="p">}</span>
<span class="nx">temp</span> <span class="o">=</span> <span class="p">{</span><span class="nx">string</span><span class="o">:</span> <span class="nx">final_string</span><span class="p">,</span> <span class="nx">width</span><span class="o">:</span> <span class="nx">new_width</span><span class="p">,</span> <span class="nx">offset</span><span class="o">:</span> <span class="nx">total_offset</span><span class="p">};</span>
<span class="nx">substrings</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">temp</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// position the overall text node</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">y_offset</span> <span class="o">=</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">y</span><span class="p">;</span>
<span class="c1">// shift by line-height to move the baseline into</span>
<span class="c1">// the bounds – otherwise the text baseline would be</span>
<span class="c1">// at the top of the bounds</span>
<span class="k">if</span><span class="p">(</span><span class="nx">line_height</span><span class="p">)</span> <span class="p">{</span><span class="nx">y_offset</span> <span class="o">+=</span> <span class="nx">line_height</span><span class="p">;}</span>
<span class="k">return</span> <span class="nx">y_offset</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// shift to the right by the padding value</span>
<span class="k">if</span><span class="p">(</span><span class="nx">padding</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">text_node_selected</span>
<span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'x'</span><span class="p">,</span> <span class="nx">bounds</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span>
<span class="p">;</span>
<span class="p">}</span>
<span class="c1">// append each substring as a tspan</span>
<span class="kd">var</span> <span class="nx">current_tspan</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">tspan_count</span><span class="p">;</span>
<span class="c1">// double check that the text content has been removed</span>
<span class="c1">// before we start appending tspans</span>
<span class="nx">text_node_selected</span><span class="p">.</span><span class="nx">text</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span>
<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="nx">substrings</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">substring</span> <span class="o">=</span> <span class="nx">substrings</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">string</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="nx">i</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">previous_substring</span> <span class="o">=</span> <span class="nx">substrings</span><span class="p">[</span><span class="nx">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span>
<span class="p">}</span>
<span class="c1">// only append if we're sure it won't make the tspans</span>
<span class="c1">// overflow the bounds.</span>
<span class="k">if</span><span class="p">((</span><span class="nx">i</span><span class="p">)</sp