Repository: ErikGartner/dTree
Branch: master
Commit: 70bbf522b176
Files: 18
Total size: 70.4 KB
Directory structure:
gitextract_xfkbn2jv/
├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .jscsrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bower.json
├── dist/
│ └── dTree.js
├── gulpfile.js
├── package.json
├── src/
│ ├── builder.js
│ └── dtree.js
└── test/
└── demo/
├── data.json
├── demo.js
└── index.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"blacklist": ["useStrict"]
}
================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: http://EditorConfig.org
root = true;
[*]
# Ensure there's no lingering whitespace
trim_trailing_whitespace = true
# Ensure a newline at the end of each file
insert_final_newline = true
[*.js]
# Unix-style newlines
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
================================================
FILE: .eslintrc
================================================
{
"parser": "babel-eslint",
"rules": {
"strict": 0,
"quotes": [2, "single"]
},
"env": {
"browser": true,
"node": true
}
}
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Commenting this out is preferred by some people, see
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
bower_components
coverage
tmp
# Users Environment Variables
.lock-wscript
.env
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
================================================
FILE: .jscsrc
================================================
{
"preset": "google",
"maximumLineLength": null
}
================================================
FILE: CHANGELOG.md
================================================
<a name="2.4.1"></a>
## [2.4.1](https://github.com/ErikGartner/dtree/compare/2.4.0...v2.4.1) (2020-04-24)
<a name="2.4.0"></a>
# [2.4.0](https://github.com/ErikGartner/dtree/compare/2.3.0...v2.4.0) (2020-04-24)
<a name="2.3.0"></a>
# [2.3.0](https://github.com/ErikGartner/dtree/compare/2.2.2...v2.3.0) (2020-04-24)
### Features
* **#20:** add feature to zoom/pan to position/node ([eb1bb87](https://github.com/ErikGartner/dtree/commit/eb1bb87))
* **#20:** add zoomToFit() function ([f57859c](https://github.com/ErikGartner/dtree/commit/f57859c))
* **#65:** add custom click event for marriage nodes ([b4111d6](https://github.com/ErikGartner/dtree/commit/b4111d6))
* **#65:** add custom renderer for marriages ([112a375](https://github.com/ErikGartner/dtree/commit/112a375))
<a name="2.2.2"></a>
## [2.2.2](https://github.com/ErikGartner/dtree/compare/2.2.1...v2.2.2) (2019-07-07)
### Bug Fixes
* Lower rectangle border thinner than the upper one in Firefox ([748a0c4](https://github.com/ErikGartner/dtree/commit/748a0c4))
* Rounding issues #92 ([9b70e04](https://github.com/ErikGartner/dtree/commit/9b70e04))
<a name="2.2.1"></a>
## [2.2.1](https://github.com/ErikGartner/dtree/compare/2.1.0...v2.2.1) (2019-05-07)
<a name="2.1.0"></a>
# [2.1.0](https://github.com/ErikGartner/dtree/compare/2.0.2...v2.1.0) (2019-02-23)
### Bug Fixes
* Bug in build script to update CDN link ([2525601](https://github.com/ErikGartner/dtree/commit/2525601))
* Missing borders in dTree demo ([5926c15](https://github.com/ErikGartner/dtree/commit/5926c15))
### Features
* Add border to SVG in demo ([7876de6](https://github.com/ErikGartner/dtree/commit/7876de6))
* Add callback for node width separation #27 ([027de86](https://github.com/ErikGartner/dtree/commit/027de86))
* Remove unused test stub code ([938395a](https://github.com/ErikGartner/dtree/commit/938395a))
* Update dependencies with Yarn ([6a207c6](https://github.com/ErikGartner/dtree/commit/6a207c6))
<a name="2.0.2"></a>
## [2.0.2](https://github.com/ErikGartner/dtree/compare/2.0.1...v2.0.2) (2017-05-02)
### Bug Fixes
* Incorrect marriages line. #53 ([25fd62e](https://github.com/ErikGartner/dtree/commit/25fd62e))
<a name="2.0.1"></a>
## [2.0.1](https://github.com/ErikGartner/dtree/compare/2.0.0...v2.0.1) (2017-04-10)
### Bug Fixes
* Missing parameters to click callback #50 ([fb00d8f](https://github.com/ErikGartner/dtree/commit/fb00d8f))
<a name="2.0.0"></a>
# [2.0.0](https://github.com/ErikGartner/dtree/compare/1.3.2...v2.0.0) (2017-03-09)
### Bug Fixes
* upgrade to d3 v4 ([d63d003](https://github.com/ErikGartner/dtree/commit/d63d003))
### Features
* Deprecated marriage attribute. Use marriages instead now. #26 ([096b90f](https://github.com/ErikGartner/dtree/commit/096b90f))
<a name="1.3.2"></a>
## [1.3.2](https://github.com/ErikGartner/dtree/compare/1.3.1...v1.3.2) (2016-07-21)
<a name="1.3.1"></a>
## [1.3.1](https://github.com/ErikGartner/dtree/compare/1.3.0...v1.3.1) (2016-03-08)
<a name="1.3.0"></a>
# [1.3.0](https://github.com/ErikGartner/dtree/compare/1.2.1...v1.3.0) (2016-03-08)
### Features
* Further improvement to height calculation ([2171995](https://github.com/ErikGartner/dtree/commit/2171995))
<a name="1.2.1"></a>
## [1.2.1](https://github.com/ErikGartner/dtree/compare/1.2.0...v1.2.1) (2016-03-08)
### Bug Fixes
* Proper height calculation #29 ([282fae5](https://github.com/ErikGartner/dtree/commit/282fae5))
<a name="1.2.0"></a>
# [1.2.0](https://github.com/ErikGartner/dtree/compare/1.1.0...v1.2.0) (2016-01-14)
### Features
* Update to lodash 4.0.0 ([bd8451d](https://github.com/ErikGartner/dtree/commit/bd8451d))
<a name="1.1.0"></a>
# [1.1.0](https://github.com/ErikGartner/dtree/compare/1.0.0...v1.1.0) (2016-01-11)
### Features
* Add support for multiple marriages #25 ([8795a5e](https://github.com/ErikGartner/dtree/commit/8795a5e))
<a name="1.0.0"></a>
# [1.0.0](https://github.com/ErikGartner/dtree/compare/0.8.1...v1.0.0) (2016-01-10)
<a name="0.8.1"></a>
## [0.8.1](https://github.com/ErikGartner/dtree/compare/0.8.0...v0.8.1) (2016-01-05)
### Bug Fixes
* Bug due improper skipping of hidden nodes ([9739472](https://github.com/ErikGartner/dtree/commit/9739472))
<a name="0.8.0"></a>
# [0.8.0](https://github.com/ErikGartner/dtree/compare/0.7.6...v0.8.0) (2016-01-05)
### Features
* Individual height of nodes depending of content ([6d9aa8a](https://github.com/ErikGartner/dtree/commit/6d9aa8a))
<a name="0.7.6"></a>
## [0.7.6](https://github.com/ErikGartner/dtree/compare/0.7.5...v0.7.6) (2016-01-04)
<a name="0.7.5"></a>
## [0.7.5](https://github.com/ErikGartner/dtree/compare/0.7.4...v0.7.5) (2016-01-04)
<a name="0.7.4"></a>
## [0.7.4](https://github.com/ErikGartner/dtree/compare/0.7.3...v0.7.4) (2016-01-04)
<a name="0.7.3"></a>
## [0.7.3](https://github.com/ErikGartner/dtree/compare/0.7.2...v0.7.3) (2016-01-04)
<a name="0.7.2"></a>
## [0.7.2](https://github.com/ErikGartner/dtree/compare/0.7.1...v0.7.2) (2016-01-04)
<a name="0.7.1"></a>
## [0.7.1](https://github.com/ErikGartner/dtree/compare/0.7.0...v0.7.1) (2016-01-04)
<a name="0.7.0"></a>
# [0.7.0](https://github.com/ErikGartner/dtree/compare/0.6.1...v0.7.0) (2016-01-04)
### Features
* Resizing nodes ([2454086](https://github.com/ErikGartner/dtree/commit/2454086))
<a name="0.6.1"></a>
## [0.6.1](https://github.com/ErikGartner/dtree/compare/0.6.0...v0.6.1) (2016-01-02)
<a name="0.6.0"></a>
# [0.6.0](https://github.com/ErikGartner/dtree/compare/0.5.0...v0.6.0) (2016-01-02)
### Bug Fixes
* Bug in demo ([b1092a4](https://github.com/ErikGartner/dtree/commit/b1092a4))
### Features
* Add depthOffset attribute ([7037257](https://github.com/ErikGartner/dtree/commit/7037257))
<a name="0.5.0"></a>
# [0.5.0](https://github.com/ErikGartner/dtree/compare/0.4.0...v0.5.0) (2016-01-01)
### Features
* Add custom sorting #7 ([9923c66](https://github.com/ErikGartner/dtree/commit/9923c66))
<a name="0.4.0"></a>
# [0.4.0](https://github.com/ErikGartner/dtree/compare/0.3.4...v0.4.0) (2016-01-01)
### Features
* Add direct children nodes #19 ([2a37451](https://github.com/ErikGartner/dtree/commit/2a37451))
<a name="0.3.4"></a>
## [0.3.4](https://github.com/ErikGartner/dtree/compare/0.3.3...v0.3.4) (2016-01-01)
### Bug Fixes
* Another gulp bug ([000165c](https://github.com/ErikGartner/dtree/commit/000165c))
<a name="0.3.3"></a>
## [0.3.3](https://github.com/ErikGartner/dtree/compare/0.3.2...v0.3.3) (2016-01-01)
### Bug Fixes
* Bug in automatic release ([97cdd26](https://github.com/ErikGartner/dtree/commit/97cdd26))
<a name="0.3.2"></a>
## [0.3.2](https://github.com/ErikGartner/dtree/compare/0.3.1...v0.3.2) (2016-01-01)
### Features
* Add automatic releasing #10 ([1e3f4b8](https://github.com/ErikGartner/dtree/commit/1e3f4b8))
<a name="0.3.1"></a>
## [0.3.1](https://github.com/ErikGartner/dtree/compare/0.3.0...v0.3.1) (2015-12-31)
### Bug Fixes
* Build including old dist ([5ca3f2a](https://github.com/ErikGartner/dtree/commit/5ca3f2a))
<a name="0.3.0"></a>
# [0.3.0](https://github.com/ErikGartner/dtree/compare/0.2.5...v0.3.0) (2015-12-31)
### Bug Fixes
* Center graph on load #11 ([1d5e71c](https://github.com/ErikGartner/dtree/commit/1d5e71c))
* Jerky zoom start ([ea184b5](https://github.com/ErikGartner/dtree/commit/ea184b5))
### Features
* Add custom html renderers and wrapping #16 #5 #3 ([5f71c8c](https://github.com/ErikGartner/dtree/commit/5f71c8c))
* Add debug variable ([51ec554](https://github.com/ErikGartner/dtree/commit/51ec554))
<a name="0.2.5"></a>
## [0.2.5](https://github.com/ErikGartner/dtree/compare/0.2.4...v0.2.5) (2015-12-30)
### Bug Fixes
* Invalid buildstep ([f593bce](https://github.com/ErikGartner/dtree/commit/f593bce))
<a name="0.2.4"></a>
## [0.2.4](https://github.com/ErikGartner/dtree/compare/0.2.3...v0.2.4) (2015-12-30)
### Features
* Add dist/ directory ([98c3d10](https://github.com/ErikGartner/dtree/commit/98c3d10))
<a name="0.2.3"></a>
## [0.2.3](https://github.com/ErikGartner/dtree/compare/0.2.2...v0.2.3) (2015-12-30)
<a name="0.2.2"></a>
## [0.2.2](https://github.com/ErikGartner/dtree/compare/0.2.1...v0.2.2) (2015-12-30)
### Features
* Add custom text function #6 ([2384b01](https://github.com/ErikGartner/dtree/commit/2384b01))
* Add version to dTree #14 ([ffd9737](https://github.com/ErikGartner/dtree/commit/ffd9737))
<a name="0.2.1"></a>
## [0.2.1](https://github.com/ErikGartner/dtree/compare/0.2.0...v0.2.1) (2015-12-29)
### Features
* Decrease node seperation ([5ca1cd0](https://github.com/ErikGartner/dtree/commit/5ca1cd0))
<a name="0.2.0"></a>
# [0.2.0](https://github.com/ErikGartner/dtree/compare/0.1.4...v0.2.0) (2015-12-27)
<a name="0.1.4"></a>
## [0.1.4](https://github.com/ErikGartner/dtree/compare/0.1.3...v0.1.4) (2015-12-27)
### Bug Fixes
* Bug when setting opts defaults ([d06bc19](https://github.com/ErikGartner/dtree/commit/d06bc19))
### Features
* Add click handler support #8 ([6908bc9](https://github.com/ErikGartner/dtree/commit/6908bc9))
* Add gulp demo command ([7b01f6d](https://github.com/ErikGartner/dtree/commit/7b01f6d))
* Improved separation between nodes ([c001db3](https://github.com/ErikGartner/dtree/commit/c001db3))
<a name="0.1.3"></a>
## [0.1.3](https://github.com/ErikGartner/dtree/compare/0.1.2...v0.1.3) (2015-12-27)
### Bug Fixes
* Bug affecting textClass ([bdfe318](https://github.com/ErikGartner/dtree/commit/bdfe318))
<a name="0.1.2"></a>
## [0.1.2](https://github.com/ErikGartner/dtree/compare/0.1.1...v0.1.2) (2015-12-27)
### Features
* Add css class override for node text ([21b8677](https://github.com/ErikGartner/dtree/commit/21b8677))
<a name="0.1.1"></a>
## [0.1.1](https://github.com/ErikGartner/dtree/compare/0.1.0...v0.1.1) (2015-12-26)
### Bug Fixes
* Typo in badge url ([e318bb4](https://github.com/ErikGartner/dtree/commit/e318bb4))
### Features
* Add release tasks to gulp ([027968c](https://github.com/ErikGartner/dtree/commit/027968c))
<a name="0.1.0"></a>
# 0.1.0 (2015-12-26)
### Bug Fixes
* Duplicates in package.json ([09be9a3](https://github.com/ErikGartner/dtree/commit/09be9a3))
* Invalid text attribute ([90f6528](https://github.com/ErikGartner/dtree/commit/90f6528))
* README.md typo ([4851fad](https://github.com/ErikGartner/dtree/commit/4851fad))
### Features
* Add "extra" field to nodes ([618b1d7](https://github.com/ErikGartner/dtree/commit/618b1d7))
* Add zooming ([f4d8aeb](https://github.com/ErikGartner/dtree/commit/f4d8aeb))
* Automatic box size ([ff38f3f](https://github.com/ErikGartner/dtree/commit/ff38f3f))
* Changed data format ([1591875](https://github.com/ErikGartner/dtree/commit/1591875))
* Iteration zero ([30899eb](https://github.com/ErikGartner/dtree/commit/30899eb))
* Switched core foundation ([ac23c28](https://github.com/ErikGartner/dtree/commit/ac23c28))
* Update devDeps ([c653f93](https://github.com/ErikGartner/dtree/commit/c653f93))
* Update options and align text ([9346c2d](https://github.com/ErikGartner/dtree/commit/9346c2d))
* Use class in data ([085ed25](https://github.com/ErikGartner/dtree/commit/085ed25))
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to dTree
## Commiting
Please follow these guidelines.
### Versioning
dTree uses [SemVer 2.0.0](http://semver.org/spec/v2.0.0.html). That is the versioning is \<MAJOR\>.\<MINOR\>.\<PATCH\>.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
Must be one of the following:
* **feat**: A new feature
* **fix**: A bug fix
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
### Scope
The scope could be anything specifying place of the commit change. For example `$location`,
`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc...
### Subject
The subject contains succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
## Recognitions
These guidelines are forked from Angular.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Erik Gärtner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# dTree
*A library for visualizing data trees with multiple parents built on top of [D3](https://github.com/mbostock/d3).*
[](https://www.npmjs.com/package/d3-dtree) [](https://github.com/ErikGartner/dTree) [](https://www.jsdelivr.com/package/npm/d3-dtree)
**Using dTree? Send me a message with a link to your website to be listed below.**
## The Online Viewer
There exists an online viewer for dTree graphs called [Treehouse](https://treehouse.gartner.io), similar to [https://bl.ocks.org/](https://bl.ocks.org/) for D3. Treehouse allows anybody to host a dTree graph without having to create a website or interact directly with the library. It fetches data from Github's gists and displays it in a nice format. All graphs are unlisted so without your Gist ID nobody else can view them. Checkout the *demo* graph for dTree:
https://treehouse.gartner.io/ErikGartner/58e58be650453b6d49d7
The same demo is also available on [JSFiddle](https://jsfiddle.net/rha8sg79/).
## Installation
There are several ways to use dTree. One way is to simply include the compiled file ```dTree.js``` that then exposes a ```dTree``` variable. dTree is available on both NPM and Bower as *d3-dtree*.
```bash
npm install d3-dtree
bower install d3-dtree
yarn add d3-dtree
```
Lastly dTree is also available through several CDNs such as [jsDelivr](https://www.jsdelivr.com/package/npm/d3-dtree):
```
https://cdn.jsdelivr.net/npm/d3-dtree@2.4.1/dist/dTree.min.js
```
## Requirements
To use the library the follow dependencies must be loaded:
- [D3](https://github.com/mbostock/d3) v4.x
- [lodash](https://github.com/lodash/lodash) v4.x
## Usage
To create a graph from data use the following command:
```javascript
tree = dTree.init(data, options);
```
The data object should have the following structure:
```json
[
{
"name": "Father", // The name of the node
"class": "node", // The CSS class of the node
"textClass": "nodeText", // The CSS class of the text in the node
"depthOffset": 1, // Generational height offset
"marriages": [
{ // Marriages is a list of nodes
"spouse":
{ // Each marriage has one spouse
"name": "Mother",
},
"children": [
{ // List of children nodes
"name": "Child",
}]
}],
"extra":
{} // Custom data passed to renderers
}]
```
The following CSS sets some good defaults:
```css
.linage {
fill: none;
stroke: black;
}
.marriage {
fill: none;
stroke: black;
}
.node {
background-color: lightblue;
border-style: solid;
border-width: 1px;
}
.nodeText{
font: 10px sans-serif;
}
.marriageNode {
background-color: black;
border-radius: 50%;
}
```
The options object has the following default values:
```javascript
{
target: '#graph',
debug: false,
width: 600,
height: 600,
hideMarriageNodes: true,
marriageNodeSize: 10,
callbacks: {
/*
Callbacks should only be overwritten on a need to basis.
See the section about callbacks below.
*/
},
margin: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
nodeWidth: 100,
styles: {
node: 'node',
linage: 'linage',
marriage: 'marriage',
text: 'nodeText'
}
}
```
### Zooming
The returned object, `tree = dTree.init(data, options)`, contains functions to control the viewport.
- `tree.resetZoom(duration = 500)` - Reset zoom and position to initial state
- `zoomTo(x, y, zoom = 1, duration = 500)` - Zoom to a specific position
- `zoomToNode(nodeId, zoom = 2, duration = 500)` - Zoom to a specific node
- `zoomToFit(duration = 500)` - Zoom to fit the entire tree into the viewport
### Callbacks
Below follows a short descriptions of the available callback functions that may be passed to dTree. See [dtree.js](https://github.com/ErikGartner/dTree/blob/master/src/dtree.js) for the *default implementations*. Information about e.g. mouse cursor position can retrieved by interacting with the `this` object, i.e. `d3.mouse(this)`.
#### nodeClick
```javascript
function(name, extra, id)
```
The nodeClick function is called by dTree when the node or text is clicked by the user. It shouldn't return any value.
#### nodeRightClick
```javascript
function(name, extra, id)
```
The nodeRightClick function is called by dTree when the node or text is right-clicked by the user. It shouldn't return any value.
#### nodeRenderer
```javascript
function(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer)
```
The nodeRenderer is called once for each node and is expected to return a string containing the node. By default the node is rendered using a div containing the text returned from the default textRendeder. See the JSFiddle above for an example on how to set the callback.
#### nodeHeightSeperation
```javascript
function(nodeWidth, nodeMaxHeight)
```
The nodeHeightSeperation is called during intial layout calculation. It shall return one number representing the distance between the levels in the graph.
#### nodeSize
```javascript
function(nodes, width, textRenderer)
```
This nodeSize function takes all nodes and a preferred width set by the user. It is then expected to return an array containing the width and height for all nodes (they all share the same width and height during layout though nodes may be rendered as smaller by the nodeRenderer).
#### nodeSorter
```javascript
function(aName, aExtra, bName, bExtra)
```
The nodeSorterer takes two nodes names and extra data, it then expected to return -1, 0 or 1 depending if A is less, equal or greater than B. This is used for sorting the nodes in the tree during layout.
#### textRenderer
```javascript
function(name, extra, textClass)
```
The textRenderer function returns the formatted text to the nodeRenderer. This way the user may chose to overwrite only what text is shown but may opt to keep the default nodeRenderer.
#### marriageClick
```javascript
function(extra, id)
```
Same as `nodeClick` but for the marriage nodes (connector).
#### marriageRightClick
```javascript
function(extra, id)
```
Same as `nodeRightClick` but for the marriage nodes (connector).
#### marriageRenderer
```javascript
function(x, y, height, width, extra, id, nodeClass)
```
Same as `nodeRenderer` but for the marriage nodes (connector).
#### marriageSize
```javascript
function(nodes, size)
```
Same as `nodeSize` but for the marriage nodes (connector).
## Built with dTree
- [🌳 dTree-Seed](https://github.com/JMHeartley/dTree-Seed) - Library to painlessly structure data for dTree, courtesy of [Justin Heartley](https://github.com/JMHeartley)!
## Development
dTree has the following development environment:
- node v11.x (use Docker [image](https://hub.docker.com/_/node/) `node:11`)
- gulp 3.x
- [Yarn](https://yarnpkg.com/) instead of npm.
To setup and build the library from scratch follow these steps:
1. ```yarn install```
2. ```yarn run build```
A demo is available by running:
```
yarn run demo
```
It hosts a demo on localhost:3000/ by serving [test/demo](test/demo) and using the latest compiled local version of the library.
## Contributing
Contributions are very welcomed! Checkout the [CONTRIBUTING](CONTRIBUTING.md) document for style information.
A good place to start is to make a pull request to solve an open issue. Feel free to ask questions regarding the issue since most have a sparse description.
## License
The MIT License (MIT)
Copyright (c) 2015-2024 Erik Gärtner
================================================
FILE: bower.json
================================================
{
"name": "d3-dtree",
"description": "A library for visualizing data trees built on top of D3.",
"main": "dist/dTree.js",
"authors": [
"Erik Gärtner"
],
"license": "MIT",
"keywords": [
"d3",
"graphing",
"tree",
"graph",
"genealogy",
"family",
"tree"
],
"homepage": "https://github.com/ErikGartner/dTree",
"moduleType": [
"amd",
"globals",
"node"
],
"dependencies": {
"lodash": "^4.0.0",
"d3": "^4.5.0"
},
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
================================================
FILE: dist/dTree.js
================================================
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.dTree = factory();
})(this, function () {
'use strict';
var TreeBuilder = (function () {
function TreeBuilder(root, siblings, opts) {
_classCallCheck(this, TreeBuilder);
TreeBuilder.DEBUG_LEVEL = opts.debug ? 1 : 0;
this.root = root;
this.siblings = siblings;
this.opts = opts;
// flatten nodes
this.allNodes = this._flatten(this.root);
// calculate node sizes
this.nodeSize = opts.callbacks.nodeSize.call(this,
// filter hidden and marriage nodes
_.filter(this.allNodes, function (node) {
return !(node.hidden || _.get(node, 'data.isMarriage'));
}), opts.nodeWidth, opts.callbacks.textRenderer);
this.marriageSize = opts.callbacks.marriageSize.call(this,
// filter hidden and non marriage nodes
_.filter(this.allNodes, function (node) {
return !node.hidden && _.get(node, 'data.isMarriage');
}), this.opts.marriageNodeSize);
}
_createClass(TreeBuilder, [{
key: 'create',
value: function create() {
var opts = this.opts;
var allNodes = this.allNodes;
var nodeSize = this.nodeSize;
var width = opts.width + opts.margin.left + opts.margin.right;
var height = opts.height + opts.margin.top + opts.margin.bottom;
// create zoom handler
var zoom = this.zoom = d3.zoom().scaleExtent([0.1, 10]).on('zoom', function () {
g.attr('transform', d3.event.transform);
});
// make a svg
var svg = this.svg = d3.select(opts.target).append('svg').attr('viewBox', [0, 0, width, height]).call(zoom);
// create svg group that holds all nodes
var g = this.g = svg.append('g');
// set zoom identity
svg.call(zoom.transform, d3.zoomIdentity.translate(width / 2, opts.margin.top).scale(1));
// Compute the layout.
this.tree = d3.tree().nodeSize([nodeSize[0] * 2, opts.callbacks.nodeHeightSeperation.call(this, nodeSize[0], nodeSize[1])]);
this.tree.separation(function separation(a, b) {
if (a.data.hidden || b.data.hidden) {
return 0.3;
} else {
return 0.6;
}
});
this._update(this.root);
}
}, {
key: '_update',
value: function _update(source) {
var opts = this.opts;
var allNodes = this.allNodes;
var nodeSize = this.nodeSize;
var marriageSize = this.marriageSize;
var treenodes = this.tree(source);
var links = treenodes.links();
// Create the link lines.
this.g.selectAll('.link').data(links).enter()
// filter links with no parents to prevent empty nodes
.filter(function (l) {
return !l.target.data.noParent;
}).append('path').attr('class', opts.styles.linage).attr('d', this._elbow);
var nodes = this.g.selectAll('.node').data(treenodes.descendants()).enter();
this._linkSiblings();
// Draw siblings (marriage)
this.g.selectAll('.sibling').data(this.siblings).enter().append('path').attr('class', opts.styles.marriage).attr('d', _.bind(this._siblingLine, this));
// Create the node rectangles.
nodes.append('foreignObject').filter(function (d) {
return d.data.hidden ? false : true;
}).attr('x', function (d) {
return Math.round(d.x - d.cWidth / 2) + 'px';
}).attr('y', function (d) {
return Math.round(d.y - d.cHeight / 2) + 'px';
}).attr('width', function (d) {
return d.cWidth + 'px';
}).attr('height', function (d) {
return d.cHeight + 'px';
}).attr('id', function (d) {
return d.id;
}).html(function (d) {
if (d.data.isMarriage) {
return opts.callbacks.marriageRenderer.call(this, d.x, d.y, marriageSize[0], marriageSize[1], d.data.extra, d.data.id, d.data['class']);
} else {
return opts.callbacks.nodeRenderer.call(this, d.data.name, d.x, d.y, nodeSize[0], nodeSize[1], d.data.extra, d.data.id, d.data['class'], d.data.textClass, opts.callbacks.textRenderer);
}
}).on('dblclick', function () {
// do not propagate a double click on a node
// to prevent the zoom from being triggered
d3.event.stopPropagation();
}).on('click', function (d) {
// ignore double-clicks and clicks on hidden nodes
if (d3.event.detail === 2 || d.data.hidden) {
return;
}
if (d.data.isMarriage) {
opts.callbacks.marriageClick.call(this, d.data.extra, d.data.id);
} else {
opts.callbacks.nodeClick.call(this, d.data.name, d.data.extra, d.data.id);
}
}).on('contextmenu', function (d) {
if (d.data.hidden) {
return;
}
d3.event.preventDefault();
if (d.data.isMarriage) {
opts.callbacks.marriageRightClick.call(this, d.data.extra, d.data.id);
} else {
opts.callbacks.nodeRightClick.call(this, d.data.name, d.data.extra, d.data.id);
}
});
}
}, {
key: '_flatten',
value: function _flatten(root) {
var n = [];
var i = 0;
function recurse(node) {
if (node.children) {
node.children.forEach(recurse);
}
if (!node.id) {
node.id = ++i;
}
n.push(node);
}
recurse(root);
return n;
}
}, {
key: '_elbow',
value: function _elbow(d, i) {
if (d.target.data.noParent) {
return 'M0,0L0,0';
}
var ny = Math.round(d.target.y + (d.source.y - d.target.y) * 0.50);
var linedata = [{
x: d.target.x,
y: d.target.y
}, {
x: d.target.x,
y: ny
}, {
x: d.source.x,
y: d.source.y
}];
var fun = d3.line().curve(d3.curveStepAfter).x(function (d) {
return d.x;
}).y(function (d) {
return d.y;
});
return fun(linedata);
}
}, {
key: '_linkSiblings',
value: function _linkSiblings() {
var allNodes = this.allNodes;
_.forEach(this.siblings, function (d) {
var start = allNodes.filter(function (v) {
return d.source.id == v.data.id;
});
var end = allNodes.filter(function (v) {
return d.target.id == v.data.id;
});
d.source.x = start[0].x;
d.source.y = start[0].y;
d.target.x = end[0].x;
d.target.y = end[0].y;
var marriageId = start[0].data.marriageNode != null ? start[0].data.marriageNode.id : end[0].data.marriageNode.id;
var marriageNode = allNodes.find(function (n) {
return n.data.id == marriageId;
});
d.source.marriageNode = marriageNode;
d.target.marriageNode = marriageNode;
});
}
}, {
key: '_siblingLine',
value: function _siblingLine(d, i) {
var ny = Math.round(d.target.y + (d.source.y - d.target.y) * 0.50);
var nodeWidth = this.nodeSize[0];
var nodeHeight = this.nodeSize[1];
// Not first marriage
if (d.number > 0) {
ny -= Math.round(nodeHeight * 8 / 10);
}
var linedata = [{
x: d.source.x,
y: d.source.y
}, {
x: Math.round(d.source.x + nodeWidth * 6 / 10),
y: d.source.y
}, {
x: Math.round(d.source.x + nodeWidth * 6 / 10),
y: ny
}, {
x: d.target.marriageNode.x,
y: ny
}, {
x: d.target.marriageNode.x,
y: d.target.y
}, {
x: d.target.x,
y: d.target.y
}];
var fun = d3.line().curve(d3.curveStepAfter).x(function (d) {
return d.x;
}).y(function (d) {
return d.y;
});
return fun(linedata);
}
}], [{
key: '_nodeHeightSeperation',
value: function _nodeHeightSeperation(nodeWidth, nodeMaxHeight) {
return nodeMaxHeight + 25;
}
}, {
key: '_nodeSize',
value: function _nodeSize(nodes, width, textRenderer) {
var maxWidth = 0;
var maxHeight = 0;
var tmpSvg = document.createElement('svg');
document.body.appendChild(tmpSvg);
_.map(nodes, function (n) {
var container = document.createElement('div');
container.setAttribute('class', n.data['class']);
container.style.visibility = 'hidden';
container.style.maxWidth = width + 'px';
var text = textRenderer(n.data.name, n.data.extra, n.data.textClass);
container.innerHTML = text;
tmpSvg.appendChild(container);
var height = container.offsetHeight;
tmpSvg.removeChild(container);
maxHeight = Math.max(maxHeight, height);
n.cHeight = height;
if (n.data.hidden) {
n.cWidth = 0;
} else {
n.cWidth = width;
}
});
document.body.removeChild(tmpSvg);
return [width, maxHeight];
}
}, {
key: '_marriageSize',
value: function _marriageSize(nodes, size) {
_.map(nodes, function (n) {
if (!n.data.hidden) {
n.cHeight = size;
n.cWidth = size;
}
});
return [size, size];
}
}, {
key: '_nodeRenderer',
value: function _nodeRenderer(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer) {
var node = '';
node += '<div ';
node += 'style="height:100%;width:100%;" ';
node += 'class="' + nodeClass + '" ';
node += 'id="node' + id + '">\n';
node += textRenderer(name, extra, textClass);
node += '</div>';
return node;
}
}, {
key: '_textRenderer',
value: function _textRenderer(name, extra, textClass) {
var node = '';
node += '<p ';
node += 'align="center" ';
node += 'class="' + textClass + '">\n';
node += name;
node += '</p>\n';
return node;
}
}, {
key: '_marriageRenderer',
value: function _marriageRenderer(x, y, height, width, extra, id, nodeClass) {
return '<div style="height:100%" class="' + nodeClass + '" id="node' + id + '"></div>';
}
}, {
key: '_debug',
value: function _debug(msg) {
if (TreeBuilder.DEBUG_LEVEL > 0) {
console.log(msg);
}
}
}]);
return TreeBuilder;
})();
var dTree = {
VERSION: '2.4.1',
init: function init(data) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var opts = _.defaultsDeep(options || {}, {
target: '#graph',
debug: false,
width: 600,
height: 600,
hideMarriageNodes: true,
callbacks: {
nodeClick: function nodeClick(name, extra, id) {},
nodeRightClick: function nodeRightClick(name, extra, id) {},
marriageClick: function marriageClick(extra, id) {},
marriageRightClick: function marriageRightClick(extra, id) {},
nodeHeightSeperation: function nodeHeightSeperation(nodeWidth, nodeMaxHeight) {
return TreeBuilder._nodeHeightSeperation(nodeWidth, nodeMaxHeight);
},
nodeRenderer: function nodeRenderer(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer) {
return TreeBuilder._nodeRenderer(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer);
},
nodeSize: function nodeSize(nodes, width, textRenderer) {
return TreeBuilder._nodeSize(nodes, width, textRenderer);
},
nodeSorter: function nodeSorter(aName, aExtra, bName, bExtra) {
return 0;
},
textRenderer: function textRenderer(name, extra, textClass) {
return TreeBuilder._textRenderer(name, extra, textClass);
},
marriageRenderer: function marriageRenderer(x, y, height, width, extra, id, nodeClass) {
return TreeBuilder._marriageRenderer(x, y, height, width, extra, id, nodeClass);
},
marriageSize: function marriageSize(nodes, size) {
return TreeBuilder._marriageSize(nodes, size);
}
},
margin: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
nodeWidth: 100,
marriageNodeSize: 10,
styles: {
node: 'node',
marriageNode: 'marriageNode',
linage: 'linage',
marriage: 'marriage',
text: 'nodeText'
}
});
var data = this._preprocess(data, opts);
var treeBuilder = new TreeBuilder(data.root, data.siblings, opts);
treeBuilder.create();
function _zoomTo(x, y) {
var zoom = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
var duration = arguments.length <= 3 || arguments[3] === undefined ? 500 : arguments[3];
treeBuilder.svg.transition().duration(duration).call(treeBuilder.zoom.transform, d3.zoomIdentity.translate(opts.width / 2, opts.height / 2).scale(zoom).translate(-x, -y));
}
return {
resetZoom: function resetZoom() {
var duration = arguments.length <= 0 || arguments[0] === undefined ? 500 : arguments[0];
treeBuilder.svg.transition().duration(duration).call(treeBuilder.zoom.transform, d3.zoomIdentity.translate(opts.width / 2, opts.margin.top).scale(1));
},
zoomTo: _zoomTo,
zoomToNode: function zoomToNode(nodeId) {
var zoom = arguments.length <= 1 || arguments[1] === undefined ? 2 : arguments[1];
var duration = arguments.length <= 2 || arguments[2] === undefined ? 500 : arguments[2];
var node = _.find(treeBuilder.allNodes, { data: { id: nodeId } });
if (node) {
_zoomTo(node.x, node.y, zoom, duration);
}
},
zoomToFit: function zoomToFit() {
var duration = arguments.length <= 0 || arguments[0] === undefined ? 500 : arguments[0];
var groupBounds = treeBuilder.g.node().getBBox();
var width = groupBounds.width;
var height = groupBounds.height;
var fullWidth = treeBuilder.svg.node().clientWidth;
var fullHeight = treeBuilder.svg.node().clientHeight;
var scale = 0.95 / Math.max(width / fullWidth, height / fullHeight);
treeBuilder.svg.transition().duration(duration).call(treeBuilder.zoom.transform, d3.zoomIdentity.translate(fullWidth / 2 - scale * (groupBounds.x + width / 2), fullHeight / 2 - scale * (groupBounds.y + height / 2)).scale(scale));
}
};
},
_preprocess: function _preprocess(data, opts) {
var siblings = [];
var id = 0;
var root = {
name: '',
id: id++,
hidden: true,
children: []
};
var reconstructTree = function reconstructTree(person, parent) {
// convert to person to d3 node
var node = {
name: person.name,
id: id++,
hidden: false,
children: [],
extra: person.extra,
textClass: person.textClass ? person.textClass : opts.styles.text,
'class': person['class'] ? person['class'] : opts.styles.node
};
// hide linages to the hidden root node
if (parent == root) {
node.noParent = true;
}
// apply depth offset
for (var i = 0; i < person.depthOffset; i++) {
var pushNode = {
name: '',
id: id++,
hidden: true,
children: [],
noParent: node.noParent
};
parent.children.push(pushNode);
parent = pushNode;
}
// sort children
dTree._sortPersons(person.children, opts);
// add "direct" children
_.forEach(person.children, function (child) {
reconstructTree(child, node);
});
parent.children.push(node);
//sort marriages
dTree._sortMarriages(person.marriages, opts);
// go through marriage
_.forEach(person.marriages, function (marriage, index) {
var m = {
name: '',
id: id++,
hidden: opts.hideMarriageNodes,
noParent: true,
children: [],
isMarriage: true,
extra: marriage.extra,
'class': marriage['class'] ? marriage['class'] : opts.styles.marriageNode
};
var sp = marriage.spouse;
var spouse = {
name: sp.name,
id: id++,
hidden: false,
noParent: true,
children: [],
textClass: sp.textClass ? sp.textClass : opts.styles.text,
'class': sp['class'] ? sp['class'] : opts.styles.node,
extra: sp.extra,
marriageNode: m
};
parent.children.push(m, spouse);
dTree._sortPersons(marriage.children, opts);
_.forEach(marriage.children, function (child) {
reconstructTree(child, m);
});
siblings.push({
source: {
id: node.id
},
target: {
id: spouse.id
},
number: index
});
});
};
_.forEach(data, function (person) {
reconstructTree(person, root);
});
return {
root: d3.hierarchy(root),
siblings: siblings
};
},
_sortPersons: function _sortPersons(persons, opts) {
if (persons != undefined) {
persons.sort(function (a, b) {
return opts.callbacks.nodeSorter.call(this, a.name, a.extra, b.name, b.extra);
});
}
return persons;
},
_sortMarriages: function _sortMarriages(marriages, opts) {
if (marriages != undefined && Array.isArray(marriages)) {
marriages.sort(function (marriageA, marriageB) {
var a = marriageA.spouse;
var b = marriageB.spouse;
return opts.callbacks.nodeSorter.call(this, a.name, a.extra, b.name, b.extra);
});
}
return marriages;
}
};
return dTree;
});
//# sourceMappingURL=dTree.js.map
================================================
FILE: gulpfile.js
================================================
// Load Gulp and all of our Gulp plugins
const gulp = require('gulp');
const $ = require('gulp-load-plugins')();
// Load other npm modules
const del = require('del');
const glob = require('glob');
const path = require('path');
const isparta = require('isparta');
const babelify = require('babelify');
const watchify = require('watchify');
const buffer = require('vinyl-buffer');
const browserify = require('browserify');
const runSequence = require('run-sequence');
const source = require('vinyl-source-stream');
const rollup = require( 'rollup' );
const argv = require('minimist')(process.argv.slice(2));
const fs = require('fs');
const conventionalGithubReleaser = require('conventional-github-releaser');
// Gather the library data from `package.json`
const manifest = require('./package.json');
const config = manifest.babelBoilerplateOptions;
const mainFile = manifest.main;
const demoFolder = manifest.demo;
const destinationFolder = path.dirname(mainFile);
const exportFileName = path.basename(mainFile, path.extname(mainFile));
// Remove the built files
gulp.task('clean', function(cb) {
del([destinationFolder], cb);
});
// Remove our temporary files
gulp.task('clean-tmp', function(cb) {
del(['tmp'], cb);
});
function createLintTask(taskName, files) {
gulp.task(taskName, function() {
return gulp.src(files)
.pipe($.plumber())
.pipe($.eslint())
.pipe($.eslint.format())
.pipe($.eslint.failOnError());
});
}
// Lint our source code
createLintTask('lint-src', ['src/**/*.js']);
function getPackageJsonVersion () {
// We parse the json file instead of using require because require caches
// multiple calls so the version number won't be updated
return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
}
// Build two versions of the library
gulp.task('build', ['lint-src', 'clean'], function(done) {
var version = getPackageJsonVersion();
rollup.rollup({
entry: 'src/' + config.entryFileName,
}).then(function(bundle) {
var res = bundle.generate({
// use this instead of `toUmd`
format: 'umd',
// this is equivalent to `strict: true` -
// optional, will be auto-detected
//exports: 'named',
// `name` -> `moduleName`
moduleName: config.mainVarName,
});
$.file(exportFileName + '.js', res.code, { src: true })
.pipe($.preprocess({context: {DTREE_VERSION: version}}))
.pipe($.plumber())
.pipe($.sourcemaps.init({ loadMaps: true }))
.pipe($.babel())
.pipe($.sourcemaps.write('./'))
.pipe(gulp.dest(destinationFolder))
.pipe($.filter(['*', '!**/*.js.map']))
.pipe($.rename(exportFileName + '.min.js'))
.pipe($.sourcemaps.init({ loadMaps: true }))
.pipe($.uglify())
.pipe($.sourcemaps.write('./'))
.pipe(gulp.dest(destinationFolder))
.pipe(gulp.dest(demoFolder, {overwrite: true}))
.on('end', done);
})
.catch(done);
});
function bundle(bundler) {
return bundler.bundle()
.on('error', function(err) {
console.log(err.message);
this.emit('end');
})
.pipe($.plumber())
.pipe(source('./tmp/__spec-build.js'))
.pipe(buffer())
.pipe(gulp.dest(''))
.pipe($.livereload());
}
// These are JS files that should be watched by Gulp. When running tests in the browser,
// watchify is used instead, so these aren't included.
const jsWatchFiles = ['src/**/*'];
// These are files other than JS files which are to be watched. They are always watched.
const otherWatchFiles = ['package.json', '**/.eslintrc'];
// Run the headless unit tests as you make changes.
gulp.task('watch', function() {
const watchFiles = jsWatchFiles.concat(otherWatchFiles);
gulp.watch(watchFiles, ['build']);
});
gulp.task('bump', function() {
return gulp.src('./package.json')
.pipe($.bump({key: 'version', type: argv.bump}))
.pipe(gulp.dest('./'));
});
gulp.task('changelog', function () {
return gulp.src('./CHANGELOG.md')
.pipe($.conventionalChangelog({
preset: 'angular',
}))
.pipe(gulp.dest('./'));
});
gulp.task('update-cdn', function() {
gulp.src(['./README.md'])
.pipe($.replace(/(\d\.\d\.\d)\/dist\/dTree.min.js/g, getPackageJsonVersion() + '/dist/dTree.min.js'))
.pipe(gulp.dest('./'));
});
gulp.task('tag-release', function (cb) {
var version = getPackageJsonVersion();
$.git.tag(version, 'Created Tag for version: ' + version, cb);
});
gulp.task('commit-changes', function () {
return gulp.src(['./README.md', './CHANGELOG.md', './dist/*'])
.pipe($.git.add())
.pipe($.git.commit('chore: Bump version number'));
});
gulp.task('prepare-release', function (callback) {
runSequence(
'bump',
'changelog',
'build',
'update-cdn',
'commit-changes',
'tag-release',
function (error) {
if (error) {
console.log(error.message);
} else {
console.log('Updated workspace for release!');
}
callback(error);
});
});
gulp.task('push-changes', function (cb) {
$.git.push('origin', 'master', cb);
});
gulp.task('push-tags', function (cb) {
$.git.push('origin', 'master', {args: '--tags'}, cb);
});
gulp.task('github-release', function (done) {
conventionalGithubReleaser({
type: 'oauth',
token: process.env.GITHUB_TOKEN
}, {
preset: 'angular'
}, done);
});
gulp.task('npm-publish', $.shell.task([
'npm publish'
]))
gulp.task('push-release', function (callback) {
runSequence(
'push-changes',
'push-tags',
'npm-publish',
'github-release',
function (error) {
if (error) {
console.log(error.message);
} else {
console.log('Release Uploaded!');
}
callback(error);
});
});
gulp.task('release', function (callback) {
runSequence(
'prepare-release',
'push-release',
function (error) {
if (error) {
console.log(error.message);
} else {
console.log('Release complete!');
}
callback(error);
});
});
gulp.task('demo', ['build'], $.shell.task([
'node test/demo/demo.js'
]))
// An alias of build
gulp.task('default', ['build']);
================================================
FILE: package.json
================================================
{
"name": "d3-dtree",
"version": "2.4.1",
"description": "A library for visualizing data trees built on top of D3.",
"main": "dist/dTree.js",
"demo": "test/demo/",
"scripts": {
"build": "gulp build",
"coverage": "gulp coverage",
"demo": "gulp demo",
"release": "gulp release"
},
"repository": {
"type": "git",
"url": "https://github.com/ErikGartner/dtree.git"
},
"keywords": [
"d3",
"graphing",
"tree graph",
"genealogy",
"family tree"
],
"author": "Erik Gärtner",
"license": "MIT",
"bugs": {
"url": "https://github.com/ErikGartner/dtree/issues"
},
"homepage": "https://github.com/ErikGartner/dtree",
"dependencies": {
"d3": "^4.5.0",
"lodash": "^4.0.0"
},
"devDependencies": {
"babel-core": "^5.2.17",
"babel-eslint": "^4.0.5",
"babelify": "^6.0.0",
"browserify": "^11.0.1",
"connect": "^3.4.0",
"conventional-changelog": "^0.5.3",
"conventional-github-releaser": "^0.5.1",
"del": "^1.1.1",
"glob": "^5.0.14",
"gulp": "^3.8.10",
"gulp-babel": "^5.0.0",
"gulp-bump": "^1.0.0",
"gulp-conventional-changelog": "^0.7.0",
"gulp-eslint": "^1.0.0",
"gulp-file": "^0.2.0",
"gulp-filter": "^3.0.0",
"gulp-git": "^2.9.0",
"gulp-github-release": "^1.1.0",
"gulp-istanbul": "^0.10.0",
"gulp-livereload": "^3.4.0",
"gulp-load-plugins": "^0.10.0",
"gulp-notify": "^2.1.0",
"gulp-plumber": "^1.0.1",
"gulp-preprocess": "^2.0.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.4",
"gulp-shell": "^0.5.0",
"gulp-sourcemaps": "^1.3.0",
"gulp-uglify": "^1.2.0",
"isparta": "~3.0.3",
"jquery": "^2.1.4",
"minimist": "^1.2.0",
"rollup": "^0.41.4",
"run-sequence": "^1.0.2",
"serve-static": "^1.10.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.0.0",
"watchify": "^3.3.1"
},
"babelBoilerplateOptions": {
"entryFileName": "dtree",
"mainVarName": "dTree",
"mochaGlobals": [
"stub",
"spy",
"expect"
]
}
}
================================================
FILE: src/builder.js
================================================
class TreeBuilder {
constructor(root, siblings, opts) {
TreeBuilder.DEBUG_LEVEL = opts.debug ? 1 : 0;
this.root = root;
this.siblings = siblings;
this.opts = opts;
// flatten nodes
this.allNodes = this._flatten(this.root);
// calculate node sizes
this.nodeSize = opts.callbacks.nodeSize.call(this,
// filter hidden and marriage nodes
_.filter(
this.allNodes,
node => !(node.hidden || _.get(node, 'data.isMarriage'))
),
opts.nodeWidth,
opts.callbacks.textRenderer
)
this.marriageSize = opts.callbacks.marriageSize.call(this,
// filter hidden and non marriage nodes
_.filter(
this.allNodes,
node => !node.hidden && _.get(node, 'data.isMarriage')
),
this.opts.marriageNodeSize
)
}
create() {
let opts = this.opts;
let allNodes = this.allNodes;
let nodeSize = this.nodeSize;
let width = opts.width + opts.margin.left + opts.margin.right;
let height = opts.height + opts.margin.top + opts.margin.bottom;
// create zoom handler
const zoom = this.zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on('zoom', function () {
g.attr('transform', d3.event.transform)
})
// make a svg
const svg = this.svg = d3.select(opts.target)
.append('svg')
.attr('viewBox', [0, 0, width, height])
.call(zoom)
// create svg group that holds all nodes
const g = this.g = svg.append('g')
// set zoom identity
svg.call(zoom.transform, d3.zoomIdentity.translate(width / 2, opts.margin.top).scale(1))
// Compute the layout.
this.tree = d3.tree()
.nodeSize([nodeSize[0] * 2,
opts.callbacks.nodeHeightSeperation.call(this, nodeSize[0], nodeSize[1])]);
this.tree.separation(function separation(a, b) {
if (a.data.hidden || b.data.hidden) {
return 0.3;
} else {
return 0.6;
}
});
this._update(this.root);
}
_update(source) {
let opts = this.opts;
let allNodes = this.allNodes;
let nodeSize = this.nodeSize;
let marriageSize = this.marriageSize;
let treenodes = this.tree(source);
let links = treenodes.links();
// Create the link lines.
this.g.selectAll('.link')
.data(links)
.enter()
// filter links with no parents to prevent empty nodes
.filter(function(l) {
return !l.target.data.noParent;
})
.append('path')
.attr('class', opts.styles.linage)
.attr('d', this._elbow);
let nodes = this.g.selectAll('.node')
.data(treenodes.descendants())
.enter();
this._linkSiblings();
// Draw siblings (marriage)
this.g.selectAll('.sibling')
.data(this.siblings)
.enter()
.append('path')
.attr('class', opts.styles.marriage)
.attr('d', _.bind(this._siblingLine, this));
// Create the node rectangles.
nodes.append('foreignObject')
.filter(function(d) {
return d.data.hidden ? false : true;
})
.attr('x', function(d) {
return Math.round(d.x - d.cWidth / 2) + 'px';
})
.attr('y', function(d) {
return Math.round(d.y - d.cHeight / 2) + 'px';
})
.attr('width', function(d) {
return d.cWidth + 'px';
})
.attr('height', function(d) {
return d.cHeight + 'px';
})
.attr('id', function(d) {
return d.id;
})
.html(function(d) {
if (d.data.isMarriage) {
return opts.callbacks.marriageRenderer.call(this,
d.x,
d.y,
marriageSize[0],
marriageSize[1],
d.data.extra,
d.data.id,
d.data.class
)
} else {
return opts.callbacks.nodeRenderer.call(this,
d.data.name,
d.x,
d.y,
nodeSize[0],
nodeSize[1],
d.data.extra,
d.data.id,
d.data.class,
d.data.textClass,
opts.callbacks.textRenderer
)
}
})
.on('dblclick', function () {
// do not propagate a double click on a node
// to prevent the zoom from being triggered
d3.event.stopPropagation()
})
.on('click', function(d) {
// ignore double-clicks and clicks on hidden nodes
if (d3.event.detail === 2 || d.data.hidden) {
return;
}
if (d.data.isMarriage) {
opts.callbacks.marriageClick.call(this, d.data.extra, d.data.id)
} else {
opts.callbacks.nodeClick.call(this, d.data.name, d.data.extra, d.data.id)
}
})
.on('contextmenu', function(d) {
if (d.data.hidden) {
return;
}
d3.event.preventDefault();
if (d.data.isMarriage) {
opts.callbacks.marriageRightClick.call(this, d.data.extra, d.data.id)
} else {
opts.callbacks.nodeRightClick.call(this, d.data.name, d.data.extra, d.data.id)
}
});
}
_flatten(root) {
let n = [];
let i = 0;
function recurse(node) {
if (node.children) {
node.children.forEach(recurse);
}
if (!node.id) {
node.id = ++i;
}
n.push(node);
}
recurse(root);
return n;
}
_elbow(d, i) {
if (d.target.data.noParent) {
return 'M0,0L0,0';
}
let ny = Math.round(d.target.y + (d.source.y - d.target.y) * 0.50);
let linedata = [{
x: d.target.x,
y: d.target.y
}, {
x: d.target.x,
y: ny
}, {
x: d.source.x,
y: d.source.y
}];
let fun = d3.line().curve(d3.curveStepAfter)
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
return fun(linedata);
}
_linkSiblings() {
let allNodes = this.allNodes;
_.forEach(this.siblings, function(d) {
let start = allNodes.filter(function(v) {
return d.source.id == v.data.id;
});
let end = allNodes.filter(function(v) {
return d.target.id == v.data.id;
});
d.source.x = start[0].x;
d.source.y = start[0].y;
d.target.x = end[0].x;
d.target.y = end[0].y;
let marriageId = (start[0].data.marriageNode != null ?
start[0].data.marriageNode.id :
end[0].data.marriageNode.id);
let marriageNode = allNodes.find(function(n) {
return n.data.id == marriageId;
});
d.source.marriageNode = marriageNode;
d.target.marriageNode = marriageNode;
});
}
_siblingLine(d, i) {
let ny = Math.round(d.target.y + (d.source.y - d.target.y) * 0.50);
let nodeWidth = this.nodeSize[0];
let nodeHeight = this.nodeSize[1];
// Not first marriage
if (d.number > 0) {
ny -= Math.round(nodeHeight * 8 / 10);
}
let linedata = [{
x: d.source.x,
y: d.source.y
}, {
x: Math.round(d.source.x + nodeWidth * 6 / 10),
y: d.source.y
}, {
x: Math.round(d.source.x + nodeWidth * 6 / 10),
y: ny
}, {
x: d.target.marriageNode.x,
y: ny
}, {
x: d.target.marriageNode.x,
y: d.target.y
}, {
x: d.target.x,
y: d.target.y
}];
let fun = d3.line().curve(d3.curveStepAfter)
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
return fun(linedata);
}
static _nodeHeightSeperation(nodeWidth, nodeMaxHeight) {
return nodeMaxHeight + 25;
}
static _nodeSize(nodes, width, textRenderer) {
let maxWidth = 0;
let maxHeight = 0;
let tmpSvg = document.createElement('svg');
document.body.appendChild(tmpSvg);
_.map(nodes, function(n) {
let container = document.createElement('div');
container.setAttribute('class', n.data.class);
container.style.visibility = 'hidden';
container.style.maxWidth = width + 'px';
let text = textRenderer(n.data.name, n.data.extra, n.data.textClass);
container.innerHTML = text;
tmpSvg.appendChild(container);
let height = container.offsetHeight;
tmpSvg.removeChild(container);
maxHeight = Math.max(maxHeight, height);
n.cHeight = height;
if (n.data.hidden) {
n.cWidth = 0;
} else {
n.cWidth = width;
}
});
document.body.removeChild(tmpSvg);
return [width, maxHeight];
}
static _marriageSize (nodes, size) {
_.map(nodes, function (n) {
if (!n.data.hidden) {
n.cHeight = size
n.cWidth = size
}
})
return [size, size]
}
static _nodeRenderer(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer) {
let node = '';
node += '<div ';
node += 'style="height:100%;width:100%;" ';
node += 'class="' + nodeClass + '" ';
node += 'id="node' + id + '">\n';
node += textRenderer(name, extra, textClass);
node += '</div>';
return node;
}
static _textRenderer(name, extra, textClass) {
let node = '';
node += '<p ';
node += 'align="center" ';
node += 'class="' + textClass + '">\n';
node += name;
node += '</p>\n';
return node;
}
static _marriageRenderer (x, y, height, width, extra, id, nodeClass) {
return `<div style="height:100%" class="${nodeClass}" id="node${id}"></div>`
}
static _debug(msg) {
if (TreeBuilder.DEBUG_LEVEL > 0) {
console.log(msg);
}
}
}
export default TreeBuilder;
================================================
FILE: src/dtree.js
================================================
import TreeBuilder from './builder.js';
const dTree = {
VERSION: '/* @echo DTREE_VERSION */',
init: function(data, options = {}) {
var opts = _.defaultsDeep(options || {}, {
target: '#graph',
debug: false,
width: 600,
height: 600,
hideMarriageNodes: true,
callbacks: {
nodeClick: function(name, extra, id) {},
nodeRightClick: function(name, extra, id) {},
marriageClick: function(extra, id) {},
marriageRightClick: function(extra, id) {},
nodeHeightSeperation: function(nodeWidth, nodeMaxHeight) {
return TreeBuilder._nodeHeightSeperation(nodeWidth, nodeMaxHeight);
},
nodeRenderer: function(name, x, y, height, width, extra, id, nodeClass, textClass, textRenderer) {
return TreeBuilder._nodeRenderer(name, x, y, height, width, extra,
id,nodeClass, textClass, textRenderer);
},
nodeSize: function(nodes, width, textRenderer) {
return TreeBuilder._nodeSize(nodes, width, textRenderer);
},
nodeSorter: function(aName, aExtra, bName, bExtra) {return 0;},
textRenderer: function(name, extra, textClass) {
return TreeBuilder._textRenderer(name, extra, textClass);
},
marriageRenderer: function (x, y, height, width, extra, id, nodeClass) {
return TreeBuilder._marriageRenderer(x, y, height, width, extra, id, nodeClass)
},
marriageSize: function (nodes, size) {
return TreeBuilder._marriageSize(nodes, size)
},
},
margin: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
nodeWidth: 100,
marriageNodeSize: 10,
styles: {
node: 'node',
marriageNode: 'marriageNode',
linage: 'linage',
marriage: 'marriage',
text: 'nodeText'
}
});
var data = this._preprocess(data, opts);
var treeBuilder = new TreeBuilder(data.root, data.siblings, opts);
treeBuilder.create();
function _zoomTo (x, y, zoom = 1, duration = 500) {
treeBuilder.svg
.transition()
.duration(duration)
.call(
treeBuilder.zoom.transform,
d3.zoomIdentity
.translate(opts.width / 2, opts.height / 2)
.scale(zoom)
.translate(-x, -y)
)
}
return {
resetZoom: function (duration = 500) {
treeBuilder.svg
.transition()
.duration(duration)
.call(
treeBuilder.zoom.transform,
d3.zoomIdentity.translate(opts.width / 2, opts.margin.top).scale(1)
)
},
zoomTo: _zoomTo,
zoomToNode: function (nodeId, zoom = 2, duration = 500) {
const node = _.find(treeBuilder.allNodes, {data: {id: nodeId}})
if (node) {
_zoomTo(node.x, node.y, zoom, duration)
}
},
zoomToFit: function (duration = 500) {
const groupBounds = treeBuilder.g.node().getBBox()
const width = groupBounds.width
const height = groupBounds.height
const fullWidth = treeBuilder.svg.node().clientWidth
const fullHeight = treeBuilder.svg.node().clientHeight
const scale = 0.95 / Math.max(width / fullWidth, height / fullHeight)
treeBuilder.svg
.transition()
.duration(duration)
.call(
treeBuilder.zoom.transform,
d3.zoomIdentity
.translate(
fullWidth / 2 - scale * (groupBounds.x + width / 2),
fullHeight / 2 - scale * (groupBounds.y + height / 2)
)
.scale(scale)
)
}
}
},
_preprocess: function(data, opts) {
var siblings = [];
var id = 0;
var root = {
name: '',
id: id++,
hidden: true,
children: []
};
var reconstructTree = function(person, parent) {
// convert to person to d3 node
var node = {
name: person.name,
id: id++,
hidden: false,
children: [],
extra: person.extra,
textClass: person.textClass ? person.textClass : opts.styles.text,
class: person.class ? person.class : opts.styles.node
};
// hide linages to the hidden root node
if (parent == root) {
node.noParent = true;
}
// apply depth offset
for (var i = 0; i < person.depthOffset; i++) {
var pushNode = {
name: '',
id: id++,
hidden: true,
children: [],
noParent: node.noParent
};
parent.children.push(pushNode);
parent = pushNode;
}
// sort children
dTree._sortPersons(person.children, opts);
// add "direct" children
_.forEach(person.children, function(child) {
reconstructTree(child, node);
});
parent.children.push(node);
//sort marriages
dTree._sortMarriages(person.marriages, opts);
// go through marriage
_.forEach(person.marriages, function(marriage, index) {
var m = {
name: '',
id: id++,
hidden: opts.hideMarriageNodes,
noParent: true,
children: [],
isMarriage: true,
extra: marriage.extra,
class: marriage.class ? marriage.class : opts.styles.marriageNode
}
var sp = marriage.spouse;
var spouse = {
name: sp.name,
id: id++,
hidden: false,
noParent: true,
children: [],
textClass: sp.textClass ? sp.textClass : opts.styles.text,
class: sp.class ? sp.class : opts.styles.node,
extra: sp.extra,
marriageNode: m
};
parent.children.push(m, spouse);
dTree._sortPersons(marriage.children, opts);
_.forEach(marriage.children, function(child) {
reconstructTree(child, m);
});
siblings.push({
source: {
id: node.id
},
target: {
id: spouse.id
},
number: index
});
});
};
_.forEach(data, function(person) {
reconstructTree(person, root);
});
return {
root: d3.hierarchy(root),
siblings: siblings
};
},
_sortPersons: function(persons, opts) {
if (persons != undefined) {
persons.sort(function(a, b) {
return opts.callbacks.nodeSorter.call(this, a.name, a.extra, b.name, b.extra);
});
}
return persons;
},
_sortMarriages: function(marriages, opts) {
if (marriages != undefined && Array.isArray(marriages)) {
marriages.sort(function(marriageA, marriageB) {
var a = marriageA.spouse;
var b = marriageB.spouse;
return opts.callbacks.nodeSorter.call(this, a.name, a.extra, b.name, b.extra);
});
}
return marriages;
}
};
export default dTree;
================================================
FILE: test/demo/data.json
================================================
[{
"name": "Niclas Superlongsurname",
"class": "man",
"textClass": "emphasis",
"marriages": [{
"spouse": {
"name": "Iliana",
"class": "woman",
"extra": {
"nickname": "Illi"
}
},
"children": [{
"name": "James",
"class": "man",
"marriages": [{
"spouse": {
"name": "Alexandra",
"class": "woman"
},
"children": [{
"name": "Eric",
"class": "man",
"marriages": [{
"spouse": {
"name": "Eva",
"class": "woman"
}
}]
}, {
"name": "Jane",
"class": "woman"
}, {
"name": "Jasper",
"class": "man"
}, {
"name": "Emma",
"class": "woman"
}, {
"name": "Julia",
"class": "woman"
}, {
"name": "Jessica",
"class": "woman"
}]
}]
}]
}]
}]
================================================
FILE: test/demo/demo.js
================================================
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname)).listen(3000);
================================================
FILE: test/demo/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<style type="text/css">
body {
font: 10px sans-serif;
}
.linage {
fill: none;
stroke: #000;
}
.marriage {
fill: none;
stroke: black;
}
.marriageNode {
background-color: black;
border-radius: 50%;
}
.man {
background-color: lightblue;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
}
.woman {
background-color: pink;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
}
.emphasis{
font-style: italic;
}
p {
padding:0;
margin:0;
}
svg {
border-style: solid;
border-width: 1px;
}
</style>
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="dTree.min.js"></script>
<body>
<h1>Demo</h1>
<div id="graph"></div>
<script>
treeJson = d3.json("data.json", function(error, treeData) {
dTree.init(treeData,
{
target: "#graph",
debug: true,
hideMarriageNodes: true,
marriageNodeSize: 5,
height: 800,
width: 1200,
callbacks: {
nodeClick: function(name, extra) {
alert('Click: ' + name);
},
nodeRightClick: function(name, extra) {
alert('Right-click: ' + name);
},
textRenderer: function(name, extra, textClass) {
if (extra && extra.nickname)
name = name + " (" + extra.nickname + ")";
return "<p align='center' class='" + textClass + "'>" + name + "</p>";
},
marriageClick: function(extra, id) {
alert('Clicked marriage node' + id);
},
marriageRightClick: function(extra, id) {
alert('Right-clicked marriage node' + id);
},
}
});
});
</script>
</body>
</html>
gitextract_xfkbn2jv/
├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .jscsrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bower.json
├── dist/
│ └── dTree.js
├── gulpfile.js
├── package.json
├── src/
│ ├── builder.js
│ └── dtree.js
└── test/
└── demo/
├── data.json
├── demo.js
└── index.html
SYMBOL INDEX (24 symbols across 4 files)
FILE: dist/dTree.js
function defineProperties (line 1) | function defineProperties(target, props) { for (var i = 0; i < props.len...
function _classCallCheck (line 3) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function TreeBuilder (line 11) | function TreeBuilder(root, siblings, opts) {
function recurse (line 151) | function recurse(node) {
function _zoomTo (line 406) | function _zoomTo(x, y) {
FILE: gulpfile.js
function createLintTask (line 39) | function createLintTask(taskName, files) {
function getPackageJsonVersion (line 52) | function getPackageJsonVersion () {
function bundle (line 95) | function bundle(bundler) {
FILE: src/builder.js
class TreeBuilder (line 1) | class TreeBuilder {
method constructor (line 3) | constructor(root, siblings, opts) {
method create (line 33) | create() {
method _update (line 78) | _update(source) {
method _flatten (line 189) | _flatten(root) {
method _elbow (line 206) | _elbow(d, i) {
method _linkSiblings (line 233) | _linkSiblings() {
method _siblingLine (line 261) | _siblingLine(d, i) {
method _nodeHeightSeperation (line 302) | static _nodeHeightSeperation(nodeWidth, nodeMaxHeight) {
method _nodeSize (line 306) | static _nodeSize(nodes, width, textRenderer) {
method _marriageSize (line 338) | static _marriageSize (nodes, size) {
method _nodeRenderer (line 349) | static _nodeRenderer(name, x, y, height, width, extra, id, nodeClass, ...
method _textRenderer (line 360) | static _textRenderer(name, extra, textClass) {
method _marriageRenderer (line 370) | static _marriageRenderer (x, y, height, width, extra, id, nodeClass) {
method _debug (line 374) | static _debug(msg) {
FILE: src/dtree.js
function _zoomTo (line 62) | function _zoomTo (x, y, zoom = 1, duration = 500) {
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (76K chars).
[
{
"path": ".babelrc",
"chars": 32,
"preview": "{\n \"blacklist\": [\"useStrict\"]\n}"
},
{
"path": ".editorconfig",
"chars": 314,
"preview": "# EditorConfig is awesome: http://EditorConfig.org\n\nroot = true;\n\n[*]\n# Ensure there's no lingering whitespace\ntrim_tra"
},
{
"path": ".eslintrc",
"chars": 147,
"preview": "{\n \"parser\": \"babel-eslint\",\n \"rules\": {\n \"strict\": 0,\n \"quotes\": [2, \"single\"]\n },\n \"env\": {\n \"browser\": t"
},
{
"path": ".gitignore",
"chars": 1552,
"preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
},
{
"path": ".jscsrc",
"chars": 54,
"preview": "{\n \"preset\": \"google\",\n \"maximumLineLength\": null\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 11182,
"preview": "<a name=\"2.4.1\"></a>\n## [2.4.1](https://github.com/ErikGartner/dtree/compare/2.4.0...v2.4.1) (2020-04-24)\n\n\n\n\n<a name=\"2"
},
{
"path": "CONTRIBUTING.md",
"chars": 2421,
"preview": "# Contributing to dTree\n\n## Commiting\nPlease follow these guidelines.\n\n### Versioning\ndTree uses [SemVer 2.0.0](http://s"
},
{
"path": "LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Erik Gärtner\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 7622,
"preview": "# dTree\n*A library for visualizing data trees with multiple parents built on top of [D3](https://github.com/mbostock/d3)"
},
{
"path": "bower.json",
"chars": 584,
"preview": "{\n \"name\": \"d3-dtree\",\n \"description\": \"A library for visualizing data trees built on top of D3.\",\n \"main\": \"dist/dTr"
},
{
"path": "dist/dTree.js",
"chars": 19502,
"preview": "var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { va"
},
{
"path": "gulpfile.js",
"chars": 6129,
"preview": "// Load Gulp and all of our Gulp plugins\nconst gulp = require('gulp');\nconst $ = require('gulp-load-plugins')();\n\n// Loa"
},
{
"path": "package.json",
"chars": 2077,
"preview": "{\n \"name\": \"d3-dtree\",\n \"version\": \"2.4.1\",\n \"description\": \"A library for visualizing data trees built on top of D3."
},
{
"path": "src/builder.js",
"chars": 9558,
"preview": "class TreeBuilder {\n\n constructor(root, siblings, opts) {\n TreeBuilder.DEBUG_LEVEL = opts.debug ? 1 : 0;\n\n this.r"
},
{
"path": "src/dtree.js",
"chars": 6945,
"preview": "import TreeBuilder from './builder.js';\n\nconst dTree = {\n\n VERSION: '/* @echo DTREE_VERSION */',\n\n init: function(data"
},
{
"path": "test/demo/data.json",
"chars": 982,
"preview": "[{\n \"name\": \"Niclas Superlongsurname\",\n \"class\": \"man\",\n \"textClass\": \"emphasis\",\n \"marriages\": [{\n \"spouse\": {\n "
},
{
"path": "test/demo/demo.js",
"chars": 129,
"preview": "var connect = require('connect');\nvar serveStatic = require('serve-static');\nconnect().use(serveStatic(__dirname)).liste"
},
{
"path": "test/demo/index.html",
"chars": 1829,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<meta charset=\"utf-8\">\n\n<style type=\"text/css\">\n\tbody {\n\t font: 10px sans-serif;\n\t}\n"
}
]
About this extraction
This page contains the full source code of the ErikGartner/dTree GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (70.4 KB), approximately 20.4k tokens, and a symbol index with 24 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.