content: import React from 'react';
  import ReactDOM from 'react-dom';
  import cn from 'classnames';
  import shallowCompare from 'react-addons-shallow-compare';
  
  const attribsToHide = ['class', 'id', 'reference'];
  
  class TreeNode extends React.Component {
      constructor(props) {
          super(props);
          this.state = {
              expanded: true,
          };
          this.toggle = this.toggle.bind(this);
          this.expandRefTarget = this.expandRefTarget.bind(this);
          this.expand = this.expand.bind(this);
          this.scrollIntoView = this.scrollIntoView.bind(this);
      }
  
      componentDidMount() {
          this.treeRootDomNode().addEventListener('expand', this.expand, false);
      }
  
      componentWillReceiveProps(nextProps) {
          if (nextProps.root !== this.props.root) {
              this.treeRootDomNode().removeEventListener('expand', this.expand, false);
              ReactDOM.findDOMNode(nextProps).addEventListener('expand', this.expand, false);
          }
      }
  
      shouldComponentUpdate(nextProps, nextState) {
          return shallowCompare(this, nextProps, nextState);
      }
  
      componentWillUnmount() {
          this.treeRootDomNode().removeEventListener('expand', this.expand, false);
      }
  
      treeRootDomNode(props = this.props) {
          return ReactDOM.findDOMNode(this.treeRoot(props));
      }
  
      treeRoot(props = this.props) {
          return props.root || this;
      }
  
      falseWhenEmpty(array) {
          return array.length > 0;
      }
  
      expandRefTarget() {
          const ref = this.props.element.attrib.reference;
          const expandEvent = new CustomEvent('expand', { detail: { ref }, bubbles: false, cancelable: true });
          this.treeRootDomNode().dispatchEvent(expandEvent);
      }
  
      expand(event) {
          const eventTargetsMe = event && event.detail.ref && event.detail.ref === this.props.element.attrib.id;
          const cascading = !event;
          if (eventTargetsMe || cascading) {
              if (!this.state.expanded) {
                  this.setState({ expanded: true });
              }
              if (this.props.expandParent) {
                  this.props.expandParent(); // cascade expansion without passing event for examination
              }
          }
          if (eventTargetsMe) {
              this.scrollIntoView();
          }
      }
  
      scrollIntoView() {
          this.treeRootDomNode().scrollIntoView(true);
      }
  
      toggle() {
          this.setState({ expanded: !this.state.expanded });
      }
  
      render() {
          const childNodes = this.props.element ?
              this.props.element.getchildren().map((element, index) =>
                  <li key={index}>
                      <TreeNode element={element} root={this.treeRoot()} expandParent={this.expand}
                        scrollParentIntoView={this.scrollIntoView}
                      />
                  </li>
              ) : null;
  
          let objectName = null;
          let objectType = null;
          let objectValue = null;
          let attribList = null;
  
          if (this.props.element.attrib.class) {
              objectName = this.props.element.tag;
              objectType = this.props.element.attrib.class;
          } else {
              objectType = this.props.element.tag;
          }
  
          if (this.props.element.text) {
              objectValue = this.props.element.text.trim() || null;
          }
  
          const attribs = this.props.element.items().filter((pair) => !attribsToHide.includes(pair[0]));
          if (attribs.length > 0) {
              attribList = attribs.map((pair) => `${pair[0]}=${pair[1]}`).join(', ');
          }
  
          const hasChildren = this.props.element && this.falseWhenEmpty(this.props.element.getchildren());
          return (
              <div>
                  <span onClick={this.props.scrollParentIntoView}
                    className={ cn({ parent: true, hidden: this === this.treeRoot() }) }
                  />
                  <span id={this.props.element.attrib.id} onClick={this.toggle}
                    className={ cn({
                        collapsible: hasChildren && this.state.expanded,
                        expandable: hasChildren && !this.state.expanded,
                        clickable: hasChildren,
                    })}
                  >
                      {objectName ? <span className="objectName">{objectName}</span> : null}
                      {objectType ? <span className="objectType">{objectType}</span> : null}
                      {objectValue ? <span className="objectValue">{objectValue}</span> : null}
                      {attribList ? <span className="attribList">{attribList}</span> : null}
                  </span>
                  {this.props.element.attrib.reference ? <span className="ref" onClick={this.expandRefTarget} /> : null}
                  <ul className={ cn({ hidden: !hasChildren || !this.state.expanded }) }>{childNodes}</ul>
              </div>
          );
      }
  }
  TreeNode.propTypes = {
      element: React.PropTypes.object.isRequired,
      expandParent: React.PropTypes.func,
      scrollParentIntoView: React.PropTypes.func,
      root: React.PropTypes.object,
  };
  
  export default TreeNode;
  