Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch for lazy node creation #1

Open
captncarrot opened this issue Aug 21, 2011 · 4 comments
Open

Patch for lazy node creation #1

captncarrot opened this issue Aug 21, 2011 · 4 comments

Comments

@captncarrot
Copy link

I need to create additional elements on the fly. So I patched treeTable a bit. Not very elegant since I am quite new to jquery ;-)

+
+  fillRow = function(tr, klass, name, content)
+  {
+    var span = document.createElement('span');
+    span.className = klass;
+    span.innerHTML = name;
+
+    var td = document.createElement('td');
+    td.appendChild(span);
+    tr.appendChild(td);
+
+    td = document.createElement('td');
+    td.innerHTML = content;
+    tr.appendChild(td);
+
+    return tr;
+  }
+
+ 
+  $.fn.treeTable.createLazyParent = function(id, klass, name, content, parentid)
+  {
+    var tr = document.createElement('tr');
+    tr.id = id;
+    if (parentid)
+      tr.className = "lazyfill parent node child-of-" + parentid;
+    else    
+      tr.className = "lazyfill parent node";

+    return fillRow(tr, klass, name, content);
+  }
+
+  $.fn.treeTable.createChild = function(id, klass, name, content, parentid, oddrow)
+  {
+    tr = document.createElement('tr');
+    tr.id = id;
+    if(parentid)
+      parentid = "child-of-" + parentid;
+
+    if(oddrow)
+      tr.className = "odd " + parentid;
+    else
+      tr.className = "even " + parentid;
+
+    return fillRow(tr, klass, name, content);
+  }
+
   $.fn.treeTable.defaults = {
     childPrefix: "child-of-",
     clickableNodeNames: false,
     expandable: true,
     indent: 19,
     initialState: "collapsed",
-    treeColumn: 0
+    treeColumn: 0,
+    lazyfiller:null
   };
-  
+
   // Recursively hide all node's children in a tree
   $.fn.collapse = function() {
     $(this).addClass("collapsed");
-    
+
     childrenOf($(this)).each(function() {
       if(!$(this).hasClass("collapsed")) {
         $(this).collapse();
       }
-      
+
       this.style.display = "none"; // Performance! $(this).hide() is slow...
     });
-    
+
     return this;
   };
-  
+
   // Recursively show all node's children in a tree
   $.fn.expand = function() {
     $(this).removeClass("collapsed").addClass("expanded");
-    
+
+    if (options.lazyfiller && $(this).hasClass("lazyfill"))
+    {
+      $(this).removeClass("lazyfill");
+      options.lazyfiller(this)
+    }
+
     childrenOf($(this)).each(function() {
       initialize($(this));
-      
+
       if($(this).is(".expanded.parent")) {
         $(this).expand();
       }

Usage is like this when adapting index.html from the sources:

    var nodectr = 1;

    lazyfillerdemo = function(parent)
    {
       tr1 = $.fn.treeTable.createLazyParent ("lazynode-" + nodectr, "folder", "node-" + nodectr, "asdfasdf n" + nodectr);
       tr2 = $.fn.treeTable.createChild("lazychild-a-" + nodectr, "folder", "child-a-" + nodectr, "asdfasdf ca" + nodectr, null, false);
       tr3 = $.fn.treeTable.createChild("lazychild-b-" + nodectr, "folder", "child-b-" + nodectr, "asdfasdf cb" + nodectr, null, true);

       nodectr++;

       $(tr3).appendBranchTo(parent[0]);
       $(tr2).appendBranchTo(parent[0]);
       $(tr1).appendBranchTo(parent[0]);
    }

    $(".example").treeTable({ lazyfiller: lazyfillerdemo});
<table class="example">
  <caption>Simple treeTable Example</caption>
  <thead>
    <tr>
      <th>TreeColumn</th>
      <th>Additional data</th>
    </tr>
  </thead>
  <tbody>
    <tr id="ex0-node-1">
      <td>Node 1: Click on the icon in front of me to expand this branch.</td>
      <td>I live in the second column.</td>
    </tr>
    <tr id="lazy-1" class="lazyfill parent">
      <td>lazy click.</td>
      <td>I live in the second column.</td>
    </tr>
    <tr id="ex0-node-1-1" class="child-of-ex0-node-1">
      <td>Node 1.1: Look, I am a table row <em>and</em> I am part of a tree!</td>
      <td>Interesting.</td>
    </tr>
    <tr id="ex0-node-1-1-1" class="child-of-ex0-node-1-1">
      <td>Node 1.1.1: I am part of the tree too!</td>
      <td>That's it!</td>
    </tr>
  </tbody>
</table>
@kbaltrinic
Copy link

I need this same functionality. captncarrot, could you fork this repo and submit a pull request with your patch?

@captncarrot
Copy link
Author

sorry, but no. Too much hassle as I have never worked with git. But I will happily give you the whole file, so you can do it :-) And I will also throw in a snippet of how to get rid of a branch - not very elegant as I do little javascript but maybe it helps a bit. I just need to figure out how I attatched the patch previously ...

@captncarrot
Copy link
Author

My current treeTable.js

/*
 * jQuery treeTable Plugin 2.3.0
 * http://ludo.cubicphuse.nl/jquery-plugins/treeTable/
 *
 * Copyright 2010, Ludo van den Boom
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * Extended for lazyfill  treetable@ewald-arnold.de   2011-08-21
 */
(function($) {
  // Helps to make options available to all functions
  // TO-DO: This gives problems when there are both expandable and non-expandable
  // trees on a page. The options shouldn't be global to all these instances!
  var options = {};
  var defaultPaddingLeft = {};

  $.fn.treeTable = function(opts) {
    options = $.extend({}, $.fn.treeTable.defaults, opts);

    return this.each(function() {
      $(this).addClass("treeTable").find("tbody tr").each(function() {
        // Initialize root nodes only if possible
        if(!options.expandable || $(this)[0].className.search(options.childPrefix) == -1) {
          // To optimize performance of indentation, I retrieve the padding-left
          // value of the first root node. This way I only have to call +css+
          // once.
          if (isNaN(defaultPaddingLeft)) {
            defaultPaddingLeft = parseInt($($(this).children("td")[options.treeColumn]).css('padding-left'), 10);
          }

          initialize($(this));
        } else if(options.initialState == "collapsed") {
          this.style.display = "none"; // Performance! $(this).hide() is slow...
        }
      });
    });
  };

  fillRow = function(tr, klass, name, content)
  {
    var span = document.createElement('span');
    span.className = klass;
    span.innerHTML = name;

    var td = document.createElement('td');
    td.appendChild(span);
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerHTML = content;
    tr.appendChild(td);

    return tr;
  };

  $.fn.treeTable.createLazyParent = function(id, klass, name, content, parentid)
  {
    var tr = document.createElement('tr');
    tr.id = id;

    if (parentid)
      tr.className = "lazyfill parent node " + options.childPrefix + parentid;
    else
      tr.className = "lazyfill parent node";

    return fillRow(tr, klass, name, content);
  };

  $.fn.treeTable.createChild = function(id, klass, name, content, parentid, oddrow)
  {
    var tr = document.createElement('tr');
    tr.id = id;

    if(parentid)
      parentid = options.childPrefix + parentid;
    else
      parentid = "";

    if(oddrow)
      tr.className = "odd " + parentid;
    else
      tr.className = "even " + parentid;

    return fillRow(tr, klass, name, content);
  };

  $.fn.treeTable.defaults = {
    childPrefix: "child-of-",
    clickableNodeNames: false,
    expandable: true,
    indent: 19,
    initialState: "collapsed",
    treeColumn: 0,
    lazyfiller:null
  };

  // Recursively hide all node's children in a tree
  $.fn.collapse = function() {
    $(this).addClass("collapsed");

    childrenOf($(this)).each(function() {
      if(!$(this).hasClass("collapsed")) {
        $(this).collapse();
      }

      this.style.display = "none"; // Performance! $(this).hide() is slow...
    });

    return this;
  };

  // Recursively show all node's children in a tree
  $.fn.expand = function() {
    $(this).removeClass("collapsed").addClass("expanded");

    if (options.lazyfiller && $(this).hasClass("lazyfill"))
    {
      $(this).removeClass("lazyfill");
      $(this).addClass("loading");
      options.lazyfiller(this);
      $(this).removeClass("loading");
   }

    childrenOf($(this)).each(function() {
      initialize($(this));

      if($(this).is(".expanded.parent")) {
        $(this).expand();
      }

      // this.style.display = "table-row"; // Unfortunately this is not possible with IE :-(
      $(this).show();
    });

    return this;
  };

  // Reveal a node by expanding all ancestors
  $.fn.reveal = function() {
    $(ancestorsOf($(this)).reverse()).each(function() {
      initialize($(this));
      $(this).expand().show();
    });

    return this;
  };

  // Add an entire branch to +destination+
  $.fn.appendBranchTo = function(destination) {
    var node = $(this);
    var parent = parentOf(node);

    var ancestorNames = $.map(ancestorsOf($(destination)), function(a) { return a.id; });

    // Conditions:
    // 1: +node+ should not be inserted in a location in a branch if this would
    //    result in +node+ being an ancestor of itself.
    // 2: +node+ should not have a parent OR the destination should not be the
    //    same as +node+'s current parent (this last condition prevents +node+
    //    from being moved to the same location where it already is).
    // 3: +node+ should not be inserted as a child of +node+ itself.
    if($.inArray(node[0].id, ancestorNames) == -1 && (!parent || (destination.id != parent[0].id)) && destination.id != node[0].id) {
      indent(node, ancestorsOf(node).length * options.indent * -1); // Remove indentation

      if(parent) { node.removeClass(options.childPrefix + parent[0].id); }

      node.addClass(options.childPrefix + destination.id);
      move(node, destination); // Recursively move nodes to new location
      indent(node, ancestorsOf(node).length * options.indent);
    }

    return this;
  };

  // Add an single top level element below tbody
  $.fn.appendTopLevelTo = function(destination) {
    var node = $(this);

    indent(node, ancestorsOf(node).length * options.indent * -1); // Remove indentation
    $(destination).children("tbody").append(node[0]);
    if (isNaN(defaultPaddingLeft))
      defaultPaddingLeft = options.indent;
    initialize(node);
    return this;
  };

  // Add reverse() function from JS Arrays
  $.fn.reverse = function() {
    return this.pushStack(this.get().reverse(), arguments);
  };

  // Toggle an entire branch
  $.fn.toggleBranch = function() {
    if($(this).hasClass("collapsed")) {
      $(this).expand();
    } else {
      $(this).removeClass("expanded").collapse();
    }

    return this;
  };

  // === Private functions

  function ancestorsOf(node) {
    var ancestors = [];
    while(node = parentOf(node)) {
      ancestors[ancestors.length] = node[0];
    }
    return ancestors;
  }

  function childrenOf(node) {
    return $("table.treeTable tbody tr." + options.childPrefix + node[0].id);
  }

  function getPaddingLeft(node) {
    var paddingLeft = parseInt(node[0].style.paddingLeft, 10);
    return (isNaN(paddingLeft)) ? defaultPaddingLeft : paddingLeft;
  }

  function indent(node, value) {
    var cell = $(node.children("td")[options.treeColumn]);
    cell[0].style.paddingLeft = getPaddingLeft(cell) + value + "px";
    childrenOf(node).each(function() {
      indent($(this), value);
    });
  }

  function initialize(node) {
    if(!node.hasClass("initialized")) {
      node.addClass("initialized");

      var childNodes = childrenOf(node);

      if(!node.hasClass("parent") && childNodes.length > 0) {
        node.addClass("parent");
      }

      if(node.hasClass("parent")) {
        var cell = $(node.children("td")[options.treeColumn]);
        var padding = getPaddingLeft(cell) + options.indent;

        childNodes.each(function() {
          $(this).children("td")[options.treeColumn].style.paddingLeft = padding + "px";
        });

        if(options.expandable) {
          cell.prepend('<span style="margin-left: -' + options.indent + 'px; padding-left: ' + options.indent + 'px" class="expander"></span>');
          $(cell[0].firstChild).click(function() { node.toggleBranch(); });

          if(options.clickableNodeNames) {
            cell[0].style.cursor = "pointer";
            $(cell).click(function(e) {
              // Don't double-toggle if the click is on the existing expander icon
              if (e.target.className != 'expander') {
                node.toggleBranch();
              }
            });
          }

          // Check for a class set explicitly by the user, otherwise set the default class
          if(!(node.hasClass("expanded") || node.hasClass("collapsed"))) {
            node.addClass(options.initialState);
          }

          if(node.hasClass("expanded")) {
            node.expand();
          }
        }
      }
    }
  }

  function move(node, destination) {
    node.insertAfter(destination);
    childrenOf(node).reverse().each(function() { move($(this), node[0]); });
  }

  function parentOf(node) {
    var classNames = node[0].className.split(' ');

    for(key in classNames) {
      if(classNames[key].match(options.childPrefix)) {
        return $("#" + classNames[key].substring(options.childPrefix.length));
      }
    }
  }
})(jQuery);

@captncarrot
Copy link
Author

current_rows contains a list with all currently existing children of a given branch.
The snippet then removes all the children that can't be found.
area contains the name of the table, parentname that of the top node of the branch.

    var allrows = $("#" + area + "-body").find("tr");
    var re = new RegExp("^" + parentname + "-[^-]*$");  // all direct children of current node start with the same name prefix
    var rows = [];

    allrows.each(function() {
      var id = this.id;
      var b1 = re.test(id);
      var b2 = jQuery.inArray(id, current_rows) >= 0;
      if (b1 && !b2)
        rows.push("#" + id);
    });

    if (rows.length != 0)
      $(rows).each(function() {
        $(this).remove();
      });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants