Element.Methods.fileTree = function(element, o, callback) {
  
  // Defaults
  if (o.root == undefined)               o.root = './';
  if (o.script == undefined)             o.script = 'demoFileTree.php';
  if (o.transitionStyle == undefined)   o.transitionStyle = 'blind';
  if (o.folderEvent == undefined)       o.folderEvent = 'click';
  if (o.transitionEffect == undefined)  o.transitionEffect = Effect.Transitions.sinoidal;
  if (o.expandSpeed == undefined)       o.expandSpeed = 0.3;
  if (o.collapseSpeed == undefined)     o.collapseSpeed = 0.3;
  if (o.loadMessage == undefined)       o.loadMessage = 'Loading...';
  

    function showTree(c, t) {
      $(c).addClassName('wait');
      
      // Send Ajax request to populate the tree through a remote script
      new Ajax.Request(o.script, {
        parameters: { dir: t },
        onSuccess: function(data) {
          
          $(c).select('.start').invoke('remove');
          $(c).removeClassName('wait').insert(data.responseText);
          
          if (t == o.root) {
            $(c).select('div').invoke('show');
          }  else {
            myInnerDiv = $(c).select('div').first();
            // Transition effect when opening a folder
            new Effect.toggle(myInnerDiv, o.transitionStyle, {
              duration: o.expandSpeed,
              queue: 'end',
              transition: o.transitionEffect,
              afterFinish: function(obj) {
                $(c).removeClassName('collapsed').addClassName('expanded');
              }
            });
          }
          
          bindTree(c);
        }
      });
    }

    function bindTree(t) {
      $(t).select('li a').each( function(element) {
        element.observe(o.folderEvent, function(e) {
        
          myUl = $(this).up(1);
          myLi = $(this).up(0);
        
          if( myLi.hasClassName('directory') ) {
            
            // Folder elements
            if( myLi.hasClassName('collapsed') ) {              
              showTree( myLi, $(this).readAttribute('rel').match( /.*\// ) );
            } else {
              collapse(myLi);
            }
          } else {
            
            // Non folder elements
            callback($(this).readAttribute('rel'));
          }
          
          Event.stop(e);
        });
      });
        
      // Prevent anchors from triggering the # on non-click events
      if( o.folderEvent.toLowerCase != 'click' ) {
        $(t).select('li a').each( function(element) { 
          element.observe('click', function(e) {
            Event.stop(e);
          });
        });
      }
    }
    
    function collapse(myLi) {
      myInnerDiv = $(myLi).select('div').first();
      
      if (myInnerDiv == null) return;

      // Collapse
      new Effect.toggle(myInnerDiv, o.transitionStyle, {
        duration: o.collapseSpeed,
        queue: 'end',
        transition: o.transitionEffect,
        afterFinish: function(obj) {
          myInnerDiv.remove();
          myLi.removeClassName('expanded').addClassName('collapsed');
        }
      });
    }

    // Loading message
    $(element).innerHTML = '<ul class="fileTree start"><li class="wait">' + o.loadMessage + '<li></ul>';
    
    // Get the initial file list
    showTree( $(element), o.root );
};

Element.addMethods();
