Use external d3-textwrap
This commit is contained in:
parent
fd5b3083dd
commit
ea11f8bff1
|
@ -69,8 +69,9 @@
|
|||
"dependencies": {
|
||||
"chalk": "^1.1.3",
|
||||
"d3": "3.5.17",
|
||||
"d3-textwrap": "^2.0.0",
|
||||
"dagre": "^0.7.4",
|
||||
"dagre-d3-renderer": "0.1.2",
|
||||
"dagre-d3-renderer": "0.1.6",
|
||||
"he": "^1.1.1",
|
||||
"lodash": "^4.17.4",
|
||||
"minimist": "^1.2.0",
|
||||
|
|
|
@ -24,463 +24,464 @@ if (!d3) {
|
|||
// }
|
||||
// log.debug('window');
|
||||
// log.debug(window);
|
||||
module.exports = d3;
|
||||
module.exports = d3
|
||||
/* jshint ignore:start */
|
||||
/*
|
||||
|
||||
D3 Text Wrap
|
||||
By Vijith Assar
|
||||
http://www.vijithassar.com
|
||||
http://www.github.com/vijithassar
|
||||
@vijithassar
|
||||
require('d3-textwrap')
|
||||
|
||||
Detailed instructions at http://www.github.com/vijithassar/d3textwrap
|
||||
// D3 Text Wrap
|
||||
// By Vijith Assar
|
||||
// http://www.vijithassar.com
|
||||
// http://www.github.com/vijithassar
|
||||
// @vijithassar
|
||||
|
||||
*/
|
||||
// Detailed instructions at http://www.github.com/vijithassar/d3textwrap
|
||||
|
||||
(function () {
|
||||
// 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
|
||||
// */
|
||||
|
||||
// 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
|
||||
}
|
||||
// (function () {
|
||||
// // 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
|
||||
|
||||
// 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
|
||||
}
|
||||
// // 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
|
||||
// }
|
||||
|
||||
// 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) {
|
||||
// default value of padding is zero if it's undefined
|
||||
var padding = parseInt(padding) || 0
|
||||
// // 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
|
||||
// }
|
||||
|
||||
// save callee into a variable so we can continue to refer to it
|
||||
// as the function scope changes
|
||||
var selection = this
|
||||
// // 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) {
|
||||
// // default value of padding is zero if it's undefined
|
||||
// var padding = parseInt(padding) || 0
|
||||
|
||||
// create a variable to store desired return values in
|
||||
var return_value
|
||||
// // save callee into a variable so we can continue to refer to it
|
||||
// // as the function scope changes
|
||||
// var selection = this
|
||||
|
||||
// extract wrap boundaries from any d3-selected rect and return them
|
||||
// in a format that matches the simpler object argument option
|
||||
var extract_bounds = function (bounds) {
|
||||
// discard the nested array wrappers added by d3
|
||||
var bounding_rect = bounds[0][0]
|
||||
// sanitize the svg element name so we can test against it
|
||||
var element_type = bounding_rect.tagName.toString()
|
||||
// if it's not a rect, exit
|
||||
if (element_type !== 'rect') {
|
||||
return false
|
||||
// if it's a rect, proceed to extracting the position attributes
|
||||
} else {
|
||||
var bounds_extracted = {}
|
||||
bounds_extracted.x = d3.select(bounding_rect).attr('x') || 0
|
||||
bounds_extracted.y = d3.select(bounding_rect).attr('y') || 0
|
||||
bounds_extracted.width = d3.select(bounding_rect).attr('width') || 0
|
||||
bounds_extracted.height = d3.select(bounding_rect).attr('height') || 0
|
||||
// also pass along the getter function
|
||||
bounds_extracted.attr = bounds.attr
|
||||
}
|
||||
return bounds_extracted
|
||||
}
|
||||
// // create a variable to store desired return values in
|
||||
// var return_value
|
||||
|
||||
// double check the input argument for the wrapping
|
||||
// boundaries to make sure it actually contains all
|
||||
// the information we'll need in order to wrap successfully
|
||||
var verify_bounds = function (bounds) {
|
||||
// quickly add a simple getter method so you can use either
|
||||
// bounds.x or bounds.attr('x') as your notation,
|
||||
// the latter being a common convention among D3
|
||||
// developers
|
||||
if (!bounds.attr) {
|
||||
bounds.attr = function (property) {
|
||||
if (this[property]) {
|
||||
return this[property]
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's an associative array, make sure it has all the
|
||||
// necessary properties represented directly
|
||||
if (
|
||||
(typeof bounds === 'object') &&
|
||||
(typeof bounds.x !== 'undefined') &&
|
||||
(typeof bounds.y !== 'undefined') &&
|
||||
(typeof bounds.width !== 'undefined') &&
|
||||
(typeof bounds.height !== 'undefined')
|
||||
// if that's the case, then the bounds are fine
|
||||
) {
|
||||
// return the lightly modified bounds
|
||||
return bounds
|
||||
// if it's a numerically indexed array, assume it's a
|
||||
// d3-selected rect and try to extract the positions
|
||||
} else if (
|
||||
// first try to make sure it's an array using Array.isArray
|
||||
(
|
||||
(typeof Array.isArray === 'function') &&
|
||||
(Array.isArray(bounds))
|
||||
) ||
|
||||
// but since Array.isArray isn't always supported, fall
|
||||
// back to casting to the object to string when it's not
|
||||
(Object.prototype.toString.call(bounds) === '[object Array]')
|
||||
) {
|
||||
// once you're sure it's an array, extract the boundaries
|
||||
// from the rect
|
||||
var extracted_bounds = extract_bounds(bounds)
|
||||
return extracted_bounds
|
||||
} else {
|
||||
// but if the bounds are neither an object nor a numerical
|
||||
// array, then the bounds argument is invalid and you'll
|
||||
// need to fix it
|
||||
return false
|
||||
}
|
||||
}
|
||||
// // extract wrap boundaries from any d3-selected rect and return them
|
||||
// // in a format that matches the simpler object argument option
|
||||
// var extract_bounds = function (bounds) {
|
||||
// // discard the nested array wrappers added by d3
|
||||
// var bounding_rect = bounds[0][0]
|
||||
// // sanitize the svg element name so we can test against it
|
||||
// var element_type = bounding_rect.tagName.toString()
|
||||
// // if it's not a rect, exit
|
||||
// if (element_type !== 'rect') {
|
||||
// return false
|
||||
// // if it's a rect, proceed to extracting the position attributes
|
||||
// } else {
|
||||
// var bounds_extracted = {}
|
||||
// bounds_extracted.x = d3.select(bounding_rect).attr('x') || 0
|
||||
// bounds_extracted.y = d3.select(bounding_rect).attr('y') || 0
|
||||
// bounds_extracted.width = d3.select(bounding_rect).attr('width') || 0
|
||||
// bounds_extracted.height = d3.select(bounding_rect).attr('height') || 0
|
||||
// // also pass along the getter function
|
||||
// bounds_extracted.attr = bounds.attr
|
||||
// }
|
||||
// return bounds_extracted
|
||||
// }
|
||||
|
||||
var apply_padding = function (bounds, padding) {
|
||||
var padded_bounds = bounds
|
||||
if (padding !== 0) {
|
||||
padded_bounds.x = parseInt(padded_bounds.x) + padding
|
||||
padded_bounds.y = parseInt(padded_bounds.y) + padding
|
||||
padded_bounds.width -= padding * 2
|
||||
padded_bounds.height -= padding * 2
|
||||
}
|
||||
return padded_bounds
|
||||
}
|
||||
// // double check the input argument for the wrapping
|
||||
// // boundaries to make sure it actually contains all
|
||||
// // the information we'll need in order to wrap successfully
|
||||
// var verify_bounds = function (bounds) {
|
||||
// // quickly add a simple getter method so you can use either
|
||||
// // bounds.x or bounds.attr('x') as your notation,
|
||||
// // the latter being a common convention among D3
|
||||
// // developers
|
||||
// if (!bounds.attr) {
|
||||
// bounds.attr = function (property) {
|
||||
// if (this[property]) {
|
||||
// return this[property]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // if it's an associative array, make sure it has all the
|
||||
// // necessary properties represented directly
|
||||
// if (
|
||||
// (typeof bounds === 'object') &&
|
||||
// (typeof bounds.x !== 'undefined') &&
|
||||
// (typeof bounds.y !== 'undefined') &&
|
||||
// (typeof bounds.width !== 'undefined') &&
|
||||
// (typeof bounds.height !== 'undefined')
|
||||
// // if that's the case, then the bounds are fine
|
||||
// ) {
|
||||
// // return the lightly modified bounds
|
||||
// return bounds
|
||||
// // if it's a numerically indexed array, assume it's a
|
||||
// // d3-selected rect and try to extract the positions
|
||||
// } else if (
|
||||
// // first try to make sure it's an array using Array.isArray
|
||||
// (
|
||||
// (typeof Array.isArray === 'function') &&
|
||||
// (Array.isArray(bounds))
|
||||
// ) ||
|
||||
// // but since Array.isArray isn't always supported, fall
|
||||
// // back to casting to the object to string when it's not
|
||||
// (Object.prototype.toString.call(bounds) === '[object Array]')
|
||||
// ) {
|
||||
// // once you're sure it's an array, extract the boundaries
|
||||
// // from the rect
|
||||
// var extracted_bounds = extract_bounds(bounds)
|
||||
// return extracted_bounds
|
||||
// } else {
|
||||
// // but if the bounds are neither an object nor a numerical
|
||||
// // array, then the bounds argument is invalid and you'll
|
||||
// // need to fix it
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
|
||||
// verify bounds
|
||||
var verified_bounds = verify_bounds(bounds)
|
||||
// var apply_padding = function (bounds, padding) {
|
||||
// var padded_bounds = bounds
|
||||
// if (padding !== 0) {
|
||||
// padded_bounds.x = parseInt(padded_bounds.x) + padding
|
||||
// padded_bounds.y = parseInt(padded_bounds.y) + padding
|
||||
// padded_bounds.width -= padding * 2
|
||||
// padded_bounds.height -= padding * 2
|
||||
// }
|
||||
// return padded_bounds
|
||||
// }
|
||||
|
||||
// modify bounds if a padding value is provided
|
||||
if (padding) {
|
||||
verified_bounds = apply_padding(verified_bounds, padding)
|
||||
}
|
||||
// // verify bounds
|
||||
// var verified_bounds = verify_bounds(bounds)
|
||||
|
||||
// check that we have the necessary conditions for this function to operate properly
|
||||
if (
|
||||
// selection it's operating on cannot be not empty
|
||||
(selection.length == 0) ||
|
||||
// d3 must be available
|
||||
(!d3) ||
|
||||
// desired wrapping bounds must be provided as an input argument
|
||||
(!bounds) ||
|
||||
// input bounds must validate
|
||||
(!verified_bounds)
|
||||
) {
|
||||
// try to return the calling selection if possible
|
||||
// so as not to interfere with methods downstream in the
|
||||
// chain
|
||||
if (selection) {
|
||||
return selection
|
||||
// if all else fails, just return false. if you hit this point then you're
|
||||
// almost certainly trying to call the textwrap() method on something that
|
||||
// doesn't make sense!
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
// if we've validated everything then we can finally proceed
|
||||
// to the meat of this operation
|
||||
} else {
|
||||
// reassign the verified bounds as the set we want
|
||||
// to work with from here on; this ensures that we're
|
||||
// using the same data structure for our bounds regardless
|
||||
// of whether the input argument was a simple object or
|
||||
// a d3 selection
|
||||
bounds = verified_bounds
|
||||
// // modify bounds if a padding value is provided
|
||||
// if (padding) {
|
||||
// verified_bounds = apply_padding(verified_bounds, padding)
|
||||
// }
|
||||
|
||||
// wrap using html and foreignObjects if they are supported
|
||||
var wrap_with_foreignobjects = function (item) {
|
||||
// establish variables to quickly reference target nodes later
|
||||
var parent = d3.select(item[0].parentNode)
|
||||
var text_node = parent.select('text')
|
||||
var styled_line_height = text_node.style('line-height')
|
||||
// extract our desired content from the single text element
|
||||
var text_to_wrap = text_node.text()
|
||||
// remove the text node and replace with a foreign object
|
||||
text_node.remove()
|
||||
var foreign_object = parent.append('foreignObject')
|
||||
// add foreign object and set dimensions, position, etc
|
||||
foreign_object
|
||||
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
||||
.attr('x', bounds.x)
|
||||
.attr('y', bounds.y)
|
||||
.attr('width', bounds.width)
|
||||
.attr('height', bounds.height)
|
||||
// insert an HTML div
|
||||
var wrap_div = foreign_object
|
||||
.append('xhtml:div')
|
||||
// this class is currently hardcoded
|
||||
// probably not necessary but easy to
|
||||
// override using .classed() and for now
|
||||
// it's nice to avoid a litany of input
|
||||
// arguments
|
||||
.attr('class', 'wrapped')
|
||||
// set div to same dimensions as foreign object
|
||||
wrap_div
|
||||
.style('height', bounds.height)
|
||||
.style('width', bounds.width)
|
||||
// insert text content
|
||||
.html(text_to_wrap)
|
||||
if (styled_line_height) {
|
||||
wrap_div.style('line-height', styled_line_height)
|
||||
}
|
||||
return_value = parent.select('foreignObject')
|
||||
}
|
||||
// // check that we have the necessary conditions for this function to operate properly
|
||||
// if (
|
||||
// // selection it's operating on cannot be not empty
|
||||
// (selection.length == 0) ||
|
||||
// // d3 must be available
|
||||
// (!d3) ||
|
||||
// // desired wrapping bounds must be provided as an input argument
|
||||
// (!bounds) ||
|
||||
// // input bounds must validate
|
||||
// (!verified_bounds)
|
||||
// ) {
|
||||
// // try to return the calling selection if possible
|
||||
// // so as not to interfere with methods downstream in the
|
||||
// // chain
|
||||
// if (selection) {
|
||||
// return selection
|
||||
// // if all else fails, just return false. if you hit this point then you're
|
||||
// // almost certainly trying to call the textwrap() method on something that
|
||||
// // doesn't make sense!
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
// // if we've validated everything then we can finally proceed
|
||||
// // to the meat of this operation
|
||||
// } else {
|
||||
// // reassign the verified bounds as the set we want
|
||||
// // to work with from here on; this ensures that we're
|
||||
// // using the same data structure for our bounds regardless
|
||||
// // of whether the input argument was a simple object or
|
||||
// // a d3 selection
|
||||
// bounds = verified_bounds
|
||||
|
||||
// wrap with tspans if foreignObject is undefined
|
||||
var wrap_with_tspans = function (item) {
|
||||
// operate on the first text item in the selection
|
||||
var text_node = item[0]
|
||||
var parent = text_node.parentNode
|
||||
var text_node_selected = d3.select(text_node)
|
||||
// measure initial size of the text node as rendered
|
||||
var text_node_height = text_node.getBBox().height
|
||||
var text_node_width = text_node.getBBox().width
|
||||
// figure out the line height, either from rendered height
|
||||
// of the font or attached styling
|
||||
var line_height
|
||||
var rendered_line_height = text_node_height
|
||||
var styled_line_height = text_node_selected.style('line-height')
|
||||
if (
|
||||
(styled_line_height) &&
|
||||
(parseInt(styled_line_height))
|
||||
) {
|
||||
line_height = parseInt(styled_line_height.replace('px', ''))
|
||||
} else {
|
||||
line_height = rendered_line_height
|
||||
}
|
||||
// only fire the rest of this if the text content
|
||||
// overflows the desired dimensions
|
||||
if (text_node_width > bounds.width) {
|
||||
// store whatever is inside the text node
|
||||
// in a variable and then zero out the
|
||||
// initial content; we'll reinsert in a moment
|
||||
// using tspan elements.
|
||||
var text_to_wrap = text_node_selected.text()
|
||||
text_node_selected.text('')
|
||||
if (text_to_wrap) {
|
||||
// keep track of whether we are splitting by spaces
|
||||
// so we know whether to reinsert those spaces later
|
||||
var break_delimiter
|
||||
// split at spaces to create an array of individual words
|
||||
var text_to_wrap_array
|
||||
if (text_to_wrap.indexOf(' ') !== -1) {
|
||||
var break_delimiter = ' '
|
||||
text_to_wrap_array = text_to_wrap.split(' ')
|
||||
} else {
|
||||
// if there are no spaces, figure out the split
|
||||
// points by comparing rendered text width against
|
||||
// bounds and translating that into character position
|
||||
// cuts
|
||||
break_delimiter = ''
|
||||
var string_length = text_to_wrap.length
|
||||
var number_of_substrings = Math.ceil(text_node_width / bounds.width)
|
||||
var splice_interval = Math.floor(string_length / number_of_substrings)
|
||||
if (
|
||||
!(splice_interval * number_of_substrings >= string_length)
|
||||
) {
|
||||
number_of_substrings++
|
||||
}
|
||||
var text_to_wrap_array = []
|
||||
var substring
|
||||
var start_position
|
||||
for (var i = 0; i < number_of_substrings; i++) {
|
||||
start_position = i * splice_interval
|
||||
substring = text_to_wrap.substr(start_position, splice_interval)
|
||||
text_to_wrap_array.push(substring)
|
||||
}
|
||||
}
|
||||
// // wrap using html and foreignObjects if they are supported
|
||||
// var wrap_with_foreignobjects = function (item) {
|
||||
// // establish variables to quickly reference target nodes later
|
||||
// var parent = d3.select(item[0].parentNode)
|
||||
// var text_node = parent.select('text')
|
||||
// var styled_line_height = text_node.style('line-height')
|
||||
// // extract our desired content from the single text element
|
||||
// var text_to_wrap = text_node.text()
|
||||
// // remove the text node and replace with a foreign object
|
||||
// text_node.remove()
|
||||
// var foreign_object = parent.append('foreignObject')
|
||||
// // add foreign object and set dimensions, position, etc
|
||||
// foreign_object
|
||||
// .attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
|
||||
// .attr('x', bounds.x)
|
||||
// .attr('y', bounds.y)
|
||||
// .attr('width', bounds.width)
|
||||
// .attr('height', bounds.height)
|
||||
// // insert an HTML div
|
||||
// var wrap_div = foreign_object
|
||||
// .append('xhtml:div')
|
||||
// // this class is currently hardcoded
|
||||
// // probably not necessary but easy to
|
||||
// // override using .classed() and for now
|
||||
// // it's nice to avoid a litany of input
|
||||
// // arguments
|
||||
// .attr('class', 'wrapped')
|
||||
// // set div to same dimensions as foreign object
|
||||
// wrap_div
|
||||
// .style('height', bounds.height)
|
||||
// .style('width', bounds.width)
|
||||
// // insert text content
|
||||
// .html(text_to_wrap)
|
||||
// if (styled_line_height) {
|
||||
// wrap_div.style('line-height', styled_line_height)
|
||||
// }
|
||||
// return_value = parent.select('foreignObject')
|
||||
// }
|
||||
|
||||
// new array where we'll store the words re-assembled into
|
||||
// substrings that have been tested against the desired
|
||||
// maximum wrapping width
|
||||
var substrings = []
|
||||
// computed text length is arguably incorrectly reported for
|
||||
// all tspans after the first one, in that they will include
|
||||
// the width of previous separate tspans. to compensate we need
|
||||
// to manually track the computed text length of all those
|
||||
// previous tspans and substrings, and then use that to offset
|
||||
// the miscalculation. this then gives us the actual correct
|
||||
// position we want to use in rendering the text in the SVG.
|
||||
var total_offset = 0
|
||||
// object for storing the results of text length computations later
|
||||
var temp = {}
|
||||
// loop through the words and test the computed text length
|
||||
// of the string against the maximum desired wrapping width
|
||||
for (var i = 0; i < text_to_wrap_array.length; i++) {
|
||||
var word = text_to_wrap_array[i]
|
||||
var previous_string = text_node_selected.text()
|
||||
var previous_width = text_node.getComputedTextLength()
|
||||
// initialize the current word as the first word
|
||||
// or append to the previous string if one exists
|
||||
var new_string
|
||||
if (previous_string) {
|
||||
new_string = previous_string + break_delimiter + word
|
||||
} else {
|
||||
new_string = word
|
||||
}
|
||||
// add the newest substring back to the text node and
|
||||
// measure the length
|
||||
text_node_selected.text(new_string)
|
||||
var new_width = text_node.getComputedTextLength()
|
||||
// adjust the length by the offset we've tracked
|
||||
// due to the misreported length discussed above
|
||||
var test_width = new_width - total_offset
|
||||
// if our latest version of the string is too
|
||||
// big for the bounds, use the previous
|
||||
// version of the string (without the newest word
|
||||
// added) and use the latest word to restart the
|
||||
// process with a new tspan
|
||||
if (new_width > bounds.width) {
|
||||
if (
|
||||
(previous_string) &&
|
||||
(previous_string !== '')
|
||||
) {
|
||||
total_offset = total_offset + previous_width
|
||||
temp = {string: previous_string, width: previous_width, offset: total_offset}
|
||||
substrings.push(temp)
|
||||
text_node_selected.text('')
|
||||
text_node_selected.text(word)
|
||||
// Handle case where there is just one more word to be wrapped
|
||||
if (i == text_to_wrap_array.length - 1) {
|
||||
new_string = word
|
||||
text_node_selected.text(new_string)
|
||||
new_width = text_node.getComputedTextLength()
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we're up to the last word in the array,
|
||||
// get the computed length as is without
|
||||
// appending anything further to it
|
||||
if (i == text_to_wrap_array.length - 1) {
|
||||
text_node_selected.text('')
|
||||
var final_string = new_string
|
||||
if (
|
||||
(final_string) &&
|
||||
(final_string !== '')
|
||||
) {
|
||||
if ((new_width - total_offset) > 0) { new_width = new_width - total_offset }
|
||||
temp = {string: final_string, width: new_width, offset: total_offset}
|
||||
substrings.push(temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
// // wrap with tspans if foreignObject is undefined
|
||||
// var wrap_with_tspans = function (item) {
|
||||
// // operate on the first text item in the selection
|
||||
// var text_node = item[0]
|
||||
// var parent = text_node.parentNode
|
||||
// var text_node_selected = d3.select(text_node)
|
||||
// // measure initial size of the text node as rendered
|
||||
// var text_node_height = text_node.getBBox().height
|
||||
// var text_node_width = text_node.getBBox().width
|
||||
// // figure out the line height, either from rendered height
|
||||
// // of the font or attached styling
|
||||
// var line_height
|
||||
// var rendered_line_height = text_node_height
|
||||
// var styled_line_height = text_node_selected.style('line-height')
|
||||
// if (
|
||||
// (styled_line_height) &&
|
||||
// (parseInt(styled_line_height))
|
||||
// ) {
|
||||
// line_height = parseInt(styled_line_height.replace('px', ''))
|
||||
// } else {
|
||||
// line_height = rendered_line_height
|
||||
// }
|
||||
// // only fire the rest of this if the text content
|
||||
// // overflows the desired dimensions
|
||||
// if (text_node_width > bounds.width) {
|
||||
// // store whatever is inside the text node
|
||||
// // in a variable and then zero out the
|
||||
// // initial content; we'll reinsert in a moment
|
||||
// // using tspan elements.
|
||||
// var text_to_wrap = text_node_selected.text()
|
||||
// text_node_selected.text('')
|
||||
// if (text_to_wrap) {
|
||||
// // keep track of whether we are splitting by spaces
|
||||
// // so we know whether to reinsert those spaces later
|
||||
// var break_delimiter
|
||||
// // split at spaces to create an array of individual words
|
||||
// var text_to_wrap_array
|
||||
// if (text_to_wrap.indexOf(' ') !== -1) {
|
||||
// var break_delimiter = ' '
|
||||
// text_to_wrap_array = text_to_wrap.split(' ')
|
||||
// } else {
|
||||
// // if there are no spaces, figure out the split
|
||||
// // points by comparing rendered text width against
|
||||
// // bounds and translating that into character position
|
||||
// // cuts
|
||||
// break_delimiter = ''
|
||||
// var string_length = text_to_wrap.length
|
||||
// var number_of_substrings = Math.ceil(text_node_width / bounds.width)
|
||||
// var splice_interval = Math.floor(string_length / number_of_substrings)
|
||||
// if (
|
||||
// !(splice_interval * number_of_substrings >= string_length)
|
||||
// ) {
|
||||
// number_of_substrings++
|
||||
// }
|
||||
// var text_to_wrap_array = []
|
||||
// var substring
|
||||
// var start_position
|
||||
// for (var i = 0; i < number_of_substrings; i++) {
|
||||
// start_position = i * splice_interval
|
||||
// substring = text_to_wrap.substr(start_position, splice_interval)
|
||||
// text_to_wrap_array.push(substring)
|
||||
// }
|
||||
// }
|
||||
|
||||
// append each substring as a tspan
|
||||
var current_tspan
|
||||
var tspan_count
|
||||
// double check that the text content has been removed
|
||||
// before we start appending tspans
|
||||
text_node_selected.text('')
|
||||
for (var i = 0; i < substrings.length; i++) {
|
||||
var substring = substrings[i].string
|
||||
if (i > 0) {
|
||||
var previous_substring = substrings[i - 1]
|
||||
}
|
||||
// only append if we're sure it won't make the tspans
|
||||
// overflow the bounds.
|
||||
if ((i) * line_height < bounds.height - (line_height * 1.5)) {
|
||||
current_tspan = text_node_selected.append('tspan')
|
||||
.text(substring)
|
||||
// vertical shift to all tspans after the first one
|
||||
current_tspan
|
||||
.attr('dy', function (d) {
|
||||
if (i > 0) {
|
||||
return line_height
|
||||
}
|
||||
})
|
||||
// shift left from default position, which
|
||||
// is probably based on the full length of the
|
||||
// text string until we make this adjustment
|
||||
current_tspan
|
||||
.attr('x', function () {
|
||||
var x_offset = bounds.x
|
||||
if (padding) { x_offset += padding }
|
||||
return x_offset
|
||||
})
|
||||
// .attr('dx', function() {
|
||||
// if(i == 0) {
|
||||
// var render_offset = 0;
|
||||
// } else if(i > 0) {
|
||||
// render_offset = substrings[i - 1].width;
|
||||
// render_offset = render_offset * -1;
|
||||
// }
|
||||
// return render_offset;
|
||||
// });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// position the overall text node, whether wrapped or not
|
||||
text_node_selected.attr('y', function () {
|
||||
var y_offset = bounds.y
|
||||
// shift by line-height to move the baseline into
|
||||
// the bounds – otherwise the text baseline would be
|
||||
// at the top of the bounds
|
||||
if (line_height) { y_offset += line_height }
|
||||
// shift by padding, if it's there
|
||||
if (padding) { y_offset += padding }
|
||||
return y_offset
|
||||
})
|
||||
// shift to the right by the padding value
|
||||
text_node_selected.attr('x', function () {
|
||||
var x_offset = bounds.x
|
||||
if (padding) { x_offset += padding }
|
||||
return x_offset
|
||||
})
|
||||
// // new array where we'll store the words re-assembled into
|
||||
// // substrings that have been tested against the desired
|
||||
// // maximum wrapping width
|
||||
// var substrings = []
|
||||
// // computed text length is arguably incorrectly reported for
|
||||
// // all tspans after the first one, in that they will include
|
||||
// // the width of previous separate tspans. to compensate we need
|
||||
// // to manually track the computed text length of all those
|
||||
// // previous tspans and substrings, and then use that to offset
|
||||
// // the miscalculation. this then gives us the actual correct
|
||||
// // position we want to use in rendering the text in the SVG.
|
||||
// var total_offset = 0
|
||||
// // object for storing the results of text length computations later
|
||||
// var temp = {}
|
||||
// // loop through the words and test the computed text length
|
||||
// // of the string against the maximum desired wrapping width
|
||||
// for (var i = 0; i < text_to_wrap_array.length; i++) {
|
||||
// var word = text_to_wrap_array[i]
|
||||
// var previous_string = text_node_selected.text()
|
||||
// var previous_width = text_node.getComputedTextLength()
|
||||
// // initialize the current word as the first word
|
||||
// // or append to the previous string if one exists
|
||||
// var new_string
|
||||
// if (previous_string) {
|
||||
// new_string = previous_string + break_delimiter + word
|
||||
// } else {
|
||||
// new_string = word
|
||||
// }
|
||||
// // add the newest substring back to the text node and
|
||||
// // measure the length
|
||||
// text_node_selected.text(new_string)
|
||||
// var new_width = text_node.getComputedTextLength()
|
||||
// // adjust the length by the offset we've tracked
|
||||
// // due to the misreported length discussed above
|
||||
// var test_width = new_width - total_offset
|
||||
// // if our latest version of the string is too
|
||||
// // big for the bounds, use the previous
|
||||
// // version of the string (without the newest word
|
||||
// // added) and use the latest word to restart the
|
||||
// // process with a new tspan
|
||||
// if (new_width > bounds.width) {
|
||||
// if (
|
||||
// (previous_string) &&
|
||||
// (previous_string !== '')
|
||||
// ) {
|
||||
// total_offset = total_offset + previous_width
|
||||
// temp = {string: previous_string, width: previous_width, offset: total_offset}
|
||||
// substrings.push(temp)
|
||||
// text_node_selected.text('')
|
||||
// text_node_selected.text(word)
|
||||
// // Handle case where there is just one more word to be wrapped
|
||||
// if (i == text_to_wrap_array.length - 1) {
|
||||
// new_string = word
|
||||
// text_node_selected.text(new_string)
|
||||
// new_width = text_node.getComputedTextLength()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // if we're up to the last word in the array,
|
||||
// // get the computed length as is without
|
||||
// // appending anything further to it
|
||||
// if (i == text_to_wrap_array.length - 1) {
|
||||
// text_node_selected.text('')
|
||||
// var final_string = new_string
|
||||
// if (
|
||||
// (final_string) &&
|
||||
// (final_string !== '')
|
||||
// ) {
|
||||
// if ((new_width - total_offset) > 0) { new_width = new_width - total_offset }
|
||||
// temp = {string: final_string, width: new_width, offset: total_offset}
|
||||
// substrings.push(temp)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// assign our modified text node with tspans
|
||||
// to the return value
|
||||
return_value = d3.select(parent).selectAll('text')
|
||||
}
|
||||
// // append each substring as a tspan
|
||||
// var current_tspan
|
||||
// var tspan_count
|
||||
// // double check that the text content has been removed
|
||||
// // before we start appending tspans
|
||||
// text_node_selected.text('')
|
||||
// for (var i = 0; i < substrings.length; i++) {
|
||||
// var substring = substrings[i].string
|
||||
// if (i > 0) {
|
||||
// var previous_substring = substrings[i - 1]
|
||||
// }
|
||||
// // only append if we're sure it won't make the tspans
|
||||
// // overflow the bounds.
|
||||
// if ((i) * line_height < bounds.height - (line_height * 1.5)) {
|
||||
// current_tspan = text_node_selected.append('tspan')
|
||||
// .text(substring)
|
||||
// // vertical shift to all tspans after the first one
|
||||
// current_tspan
|
||||
// .attr('dy', function (d) {
|
||||
// if (i > 0) {
|
||||
// return line_height
|
||||
// }
|
||||
// })
|
||||
// // shift left from default position, which
|
||||
// // is probably based on the full length of the
|
||||
// // text string until we make this adjustment
|
||||
// current_tspan
|
||||
// .attr('x', function () {
|
||||
// var x_offset = bounds.x
|
||||
// if (padding) { x_offset += padding }
|
||||
// return x_offset
|
||||
// })
|
||||
// // .attr('dx', function() {
|
||||
// // if(i == 0) {
|
||||
// // var render_offset = 0;
|
||||
// // } else if(i > 0) {
|
||||
// // render_offset = substrings[i - 1].width;
|
||||
// // render_offset = render_offset * -1;
|
||||
// // }
|
||||
// // return render_offset;
|
||||
// // });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // position the overall text node, whether wrapped or not
|
||||
// text_node_selected.attr('y', function () {
|
||||
// var y_offset = bounds.y
|
||||
// // shift by line-height to move the baseline into
|
||||
// // the bounds – otherwise the text baseline would be
|
||||
// // at the top of the bounds
|
||||
// if (line_height) { y_offset += line_height }
|
||||
// // shift by padding, if it's there
|
||||
// if (padding) { y_offset += padding }
|
||||
// return y_offset
|
||||
// })
|
||||
// // shift to the right by the padding value
|
||||
// text_node_selected.attr('x', function () {
|
||||
// var x_offset = bounds.x
|
||||
// if (padding) { x_offset += padding }
|
||||
// return x_offset
|
||||
// })
|
||||
|
||||
// variable used to hold the functions that let us
|
||||
// switch between the wrap methods
|
||||
var wrap_method
|
||||
// // assign our modified text node with tspans
|
||||
// // to the return value
|
||||
// return_value = d3.select(parent).selectAll('text')
|
||||
// }
|
||||
|
||||
// if a wrap method if being forced, assign that
|
||||
// function
|
||||
if (force_wrap_method) {
|
||||
if (force_wrap_method == 'foreignobjects') {
|
||||
wrap_method = wrap_with_foreignobjects
|
||||
} else if (force_wrap_method == 'tspans') {
|
||||
wrap_method = wrap_with_tspans
|
||||
}
|
||||
}
|
||||
// // variable used to hold the functions that let us
|
||||
// // switch between the wrap methods
|
||||
// var wrap_method
|
||||
|
||||
// if no wrap method is being forced, then instead
|
||||
// test for browser support of foreignobject and
|
||||
// use whichever wrap method makes sense accordingly
|
||||
if (!force_wrap_method) {
|
||||
if (typeof SVGForeignObjectElement !== 'undefined') {
|
||||
wrap_method = wrap_with_foreignobjects
|
||||
} else {
|
||||
wrap_method = wrap_with_tspans
|
||||
}
|
||||
}
|
||||
// // if a wrap method if being forced, assign that
|
||||
// // function
|
||||
// if (force_wrap_method) {
|
||||
// if (force_wrap_method == 'foreignobjects') {
|
||||
// wrap_method = wrap_with_foreignobjects
|
||||
// } else if (force_wrap_method == 'tspans') {
|
||||
// wrap_method = wrap_with_tspans
|
||||
// }
|
||||
// }
|
||||
|
||||
// run the desired wrap function for each item
|
||||
// in the d3 selection that called .textwrap()
|
||||
for (var i = 0; i < selection.length; i++) {
|
||||
var item = selection[i]
|
||||
wrap_method(item)
|
||||
}
|
||||
// // if no wrap method is being forced, then instead
|
||||
// // test for browser support of foreignobject and
|
||||
// // use whichever wrap method makes sense accordingly
|
||||
// if (!force_wrap_method) {
|
||||
// if (typeof SVGForeignObjectElement !== 'undefined') {
|
||||
// wrap_method = wrap_with_foreignobjects
|
||||
// } else {
|
||||
// wrap_method = wrap_with_tspans
|
||||
// }
|
||||
// }
|
||||
|
||||
// return the modified nodes so we can chain other
|
||||
// methods to them.
|
||||
return return_value
|
||||
}
|
||||
}
|
||||
})()
|
||||
/* jshint ignore:end */
|
||||
// // run the desired wrap function for each item
|
||||
// // in the d3 selection that called .textwrap()
|
||||
// for (var i = 0; i < selection.length; i++) {
|
||||
// var item = selection[i]
|
||||
// wrap_method(item)
|
||||
// }
|
||||
|
||||
// // return the modified nodes so we can chain other
|
||||
// // methods to them.
|
||||
// return return_value
|
||||
// }
|
||||
// }
|
||||
// })()
|
||||
// /* jshint ignore:end */
|
||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -2113,25 +2113,31 @@ custom-event@~1.0.0:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
|
||||
|
||||
d3-selection@^1.0.2:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.0.5.tgz#948c73b41a44e28d1742ae2ff207c2aebca2734b"
|
||||
|
||||
d3-textwrap@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-textwrap/-/d3-textwrap-2.0.0.tgz#29d54d2cdc32ce806d63921b8740e473f059b653"
|
||||
dependencies:
|
||||
d3-selection "^1.0.2"
|
||||
|
||||
d3@3.5.17:
|
||||
version "3.5.17"
|
||||
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
|
||||
|
||||
d3@3.5.6:
|
||||
version "3.5.6"
|
||||
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.6.tgz#9451c651ca733fb9672c81fb7f2655164a73a42d"
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
dependencies:
|
||||
es5-ext "^0.10.9"
|
||||
|
||||
dagre-d3-renderer@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.1.2.tgz#543fc06f6a7c9acc916c507c4b33b837705ad5cf"
|
||||
dagre-d3-renderer@0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.1.6.tgz#97e807c4b7462d0af1d3000b7e537488648fc195"
|
||||
dependencies:
|
||||
d3 "3.5.6"
|
||||
d3 "3.5.17"
|
||||
dagre "^0.7.4"
|
||||
graphlib "^2.1.1"
|
||||
lodash "^4.17.4"
|
||||
|
|
Loading…
Reference in New Issue