UNPKG

mermaid

Version:

Markdownish syntax for generating flowcharts, sequence diagrams and gantt charts.

421 lines (406 loc) 57.9 kB
<!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">&#182;</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">&#182;</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 &#39;tspan&#39; to // use tspans and &#39;foreignobject&#39; to use foreignobject var force_wrap_method = false; // by default no wrap method is forced force_wrap_method = &#39;tspans&#39;; // uncomment this statement to force tspans // force_wrap_method = &#39;foreignobjects&#39;; // 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&#39;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 == &#39;undefined&#39;) { 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&#39;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&#39;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">&#39;rect&#39;</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&#39;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">&#39;x&#39;</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">&#39;y&#39;</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">&#39;width&#39;</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">&#39;height&#39;</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&#39;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(&#39;x&#39;) 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&#39;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">&#39;object&#39;</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">&#39;undefined&#39;</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">&#39;undefined&#39;</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">&#39;undefined&#39;</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">&#39;undefined&#39;</span><span class="p">)</span> <span class="c1">// if that&#39;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&#39;s a numerically indexed array, assume it&#39;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&#39;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">&#39;function&#39;</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&#39;t always supported, fall</span> <span class="c1">// back to casting to the object to string when it&#39;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">&#39;[object Array]&#39;</span><span class="p">)</span> <span class="p">)</span> <span class="p">{</span> <span class="c1">// once you&#39;re sure it&#39;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&#39;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&#39;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&#39;re</span> <span class="c1">// almost certainly trying to call the textwrap() method on something that</span> <span class="c1">// doesn&#39;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&#39;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&#39;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">&#39;text&#39;</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">&#39;line-height&#39;</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">&#39;foreignObject&#39;</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">&#39;x&#39;</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">&#39;y&#39;</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">&#39;width&#39;</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">&#39;height&#39;</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">&#39;xhtml:div&#39;</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&#39;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">&#39;class&#39;</span><span class="p">,</span> <span class="s1">&#39;wrapped&#39;</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">&#39;height&#39;</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">&#39;width&#39;</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">&#39;line-height&#39;</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">&#39;foreignObject&#39;</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">&#39;line-height&#39;</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">&#39;px&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</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&#39;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">&#39;&#39;</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">&#39; &#39;</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">&#39; &#39;</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">&#39; &#39;</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">&#39;&#39;</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&#39;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&#39;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">&#39;&#39;</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">&#39;&#39;</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&#39;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">&#39;&#39;</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">&#39;&#39;</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">&#39;y&#39;</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">&#39;x&#39;</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">&#39;&#39;</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&#39;re sure it won&#39;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