Source Code

for file /controls/TreeView.js

// TreeView.js
// Javascript Behaviour for the TreeView Control
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// ----- 
// 03.01.2006 created by Matthias Hertel
// 04.01.2006 FireFox compatible.
// 25.08.2007 supporting more attributes title, link, img on file nodes.
// 26.08.2007 supporting OpenAjax hub events.

var TreeViewBehaviour = {
  /// <summary>Implementation of a JavaScript Behavior for a treeview control.
  /// This control implements a treeview that is dynamically expanded with sub nodes
  /// when the user clicks on an existing non-expanded node.
  /// The new subnodes are retrieved by using a webservice call.</summary>

  _namespace: "de.mathertel.treeview.", /// <summary>The root namespace for treview events.</summary>

  // ----- Properties -----
  service: "", /// <summary>Name or object reference to the WebService function proxy.</summary>

  init: function () {
    /// <summary>Initialize the control.</summary>
    this.ExploreAction.call = this.service;
  }, // init

  // ----- Events -----

  onclick: function (evt) {
    /// <summary>Handle clicks on folder or file lines.</summary>
    evt = evt || window.event;
    var t = evt.target || evt.srcElement;

    if ((t.href != null) && (t.href != "") && (t.href != window.location.href + "#")) {
      return; // just follow the link
    }

    // skip some inner elements without any functionality
    while ((t != null) && ((t.className == "") || (t.className == "ft")))
      t = t.parentNode;

    // clicked on a file ?
    if (t.className.substr(0, 2) == "fl") {
      var path = TreeViewBehaviour._nodePath(t);
      OpenAjax.hub.publish(this._namespace + "click", path);
    }

    // clicked on a folder ?
    if ((t.className != "do") && (t.className != "dc") && (t.className != "du"))
      t = t.parentNode;

    if ((t.className == "do") || (t.className == "dc") || (t.className == "du")) {
      var subf = t.nextSibling;
      if ((subf != null) && (subf.nodeName != "DIV"))
        subf = subf.nextSibling;

      if ((subf == null) || (subf.nodeName != "DIV")) {
        // this should never happen

      } else if (t.className == "do") {
        // hide subtree
        t.className = "dc";
        subf.style.display = "none";

      } else if (t.className == "dc") {
        // show subtree
        t.className = "do";
        subf.style.display = "block";

      } else if (t.className == "du") {
        // start loading a new subtree fragment
        t.className = "do";
        subf.innerText = "loading...";
        subf.style.display = "block";
        ajax.Start(this.ExploreAction, t);
      }
    } // if

    // cancle the standard click actions.
    evt.cancelBubble = true;
    evt.returnValue = false;
  }, // onchange


  xslt: "<?xml version='1.0' ?><xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>\
    <xsl:template match='/tree'><xsl:apply-templates /></xsl:template>\
    <xsl:template match='folder[@title]'><div class='du'><xsl:attribute name='name'><xsl:value-of select='@name' /></xsl:attribute><span class='ft'><xsl:value-of select='@title' /></span></div><div class='subframe' style='display:none'></div></xsl:template>\
    <xsl:template match='folder'><div class='du'><xsl:attribute name='name'><xsl:value-of select='@name' /></xsl:attribute><span class='ft'><xsl:value-of select='@name' /></span></div><div class='subframe' style='display:none'><xsl:apply-templates /></div></xsl:template>\
    <xsl:template match='file'><div class='fl'>\
    <xsl:attribute name='class'>fl <xsl:value-of select='@img' /></xsl:attribute>\
    <xsl:attribute name='name'><xsl:value-of select='@name' /></xsl:attribute><span class='ft'>\
    <xsl:choose>\
    <xsl:when test='@link'><a><xsl:attribute name='href'><xsl:value-of select='@link' /></xsl:attribute>\
    <xsl:choose><xsl:when test='@title'><xsl:value-of select='@title' /></xsl:when><xsl:otherwise><xsl:value-of select='@name' /></xsl:otherwise></xsl:choose>\
    </a>\</xsl:when>\
    <xsl:otherwise><xsl:choose><xsl:when test='@title'><xsl:value-of select='@title' /></xsl:when><xsl:otherwise><xsl:value-of select='@name' /></xsl:otherwise></xsl:choose>\
    </xsl:otherwise></xsl:choose>\
    </span></div></xsl:template>\
    </xsl:stylesheet>", /// <summary>The xslt that is used to transfer the retrieved nodes into valid HTML code.</summary>


  // ----- Methods -----

  ExtendTree: function (src, data) {
    /// <summary>Extend the src node by the given xml structure.</summary>
    var subf = src.nextSibling;
    if ((subf != null) && (subf.nodeName != "DIV"))
      subf = subf.nextSibling;

    if (window.XSLTProcessor) {
      // Mozilla...

      if (typeof (this.xslt) == "string") {
        this.xslt = ajax._getXMLDOM(this.xslt);
      } // if

      // Finally import the .xsl
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(this.xslt);

      var fragment = xsltProcessor.transformToFragment(data, window.document);
      subf.innerHTML = "";
      subf.appendChild(fragment);

    } else if (window.ActiveXObject || "ActiveXObject" in window) {
      // load xslt text into a XSLTemplate
      var xslt = new ActiveXObject("Msxml2.XSLTemplate");
      var xsltDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
      xsltDoc.async = false;
      xsltDoc.loadXML(this.xslt);
      xslt.stylesheet = xsltDoc;

      // data + xlst => html
      var xslProc = xslt.createProcessor();
      xslProc.input = data;
      xslProc.transform();
      subf.innerHTML = xslProc.output;

    } else {
      // IE
      if (typeof (this.xslt) == "string") {
        var xsltDoc = new ActiveXObject("Msxml2.DOMDocument");
        xsltDoc.async = false;
        xsltDoc.loadXML(this.xslt);
        this.xslt = xsltDoc;
      } // if

      subf.innerHTML = data.transformNode(this.xslt);
    } // if

    if (subf.childNodes.length == 0) {
      src.className = "de";
      subf.style.display = "none";
    } // if

  }, // ExtendTree


  _nodePath: function (src) {
    /// <summary>Build the full path name for a given node.</summary>
    var path = "";
    while ((src != null) && (src._attachedBehaviour != TreeViewBehaviour)) {
      if (src.nodeType == 3) {
        src = src.previousSibling;

      } else if (src.className == "subframe") {
        src = src.previousSibling;

      } else if ((src.className == "do") || (src.className.substr(0, 2) == "fl")) {
        // append the path by the name of this file or folder node.
        path = "/" + src.attributes["name"].value + path;
        src = src.parentNode;

      } else {
        src = src.parentNode;
      } // if
    }
    while (path.substr(0, 2) == "//")
      path = path.substr(1);
    return (path);
  }, // _nodePath

  // ----- AJAX Actions -----

  ExploreAction: {
    /// <summary>The ajax action that is used to retrieve the sub-nodes of a given folder.</summary>
    delay: 10,
    queueMultiple: true,

    prepare: function (src) { return (TreeViewBehaviour._nodePath(src)); },
    call: "",
    finish:
     function (data, src) {
       jcl.FindBehaviourElement(src, TreeViewBehaviour).ExtendTree(src, data);
     },

    onException: proxies.alertException
  } // ExploreAction

}; // TreeViewBehaviour


This page is part of the http://www.mathertel.de/ web site.

For updates and discussions see http://ajaxaspects.blogspot.com/.