text: /*!
   * jquery.fancytree.table.js
   *
   * Render tree as table (aka 'treegrid', 'tabletree').
   * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
   *
   * Copyright (c) 2008-2015, Martin Wendt (http://wwWendt.de)
   *
   * Released under the MIT license
   * https://github.com/mar10/fancytree/wiki/LicenseInfo
   *
   * @version @VERSION
   * @date @DATE
   */
  
  ;(function($, window, document, undefined) {
  
  "use strict";
  
  /* *****************************************************************************
   * Private functions and variables
   */
  function _assert(cond, msg){
  	msg = msg || "";
  	if(!cond){
  		$.error("Assertion failed " + msg);
  	}
  }
  
  function insertSiblingAfter(referenceNode, newNode) {
  	referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  }
  
  /* Show/hide all rows that are structural descendants of `parent`. */
  function setChildRowVisibility(parent, flag) {
  	parent.visit(function(node){
  		var tr = node.tr;
  		// currentFlag = node.hide ? false : flag; // fix for ext-filter
  		if(tr){
  			tr.style.display = (node.hide || !flag) ? "none" : "";
  		}
  		if(!node.expanded){
  			return "skip";
  		}
  	});
  }
  
  /* Find node that is rendered in previous row. */
  function findPrevRowNode(node){
  	var i, last, prev,
  		parent = node.parent,
  		siblings = parent ? parent.children : null;
  
  	if(siblings && siblings.length > 1 && siblings[0] !== node){
  		// use the lowest descendant of the preceeding sibling
  		i = $.inArray(node, siblings);
  		prev = siblings[i - 1];
  		_assert(prev.tr);
  		// descend to lowest child (with a <tr> tag)
  		while(prev.children){
  			last = prev.children[prev.children.length - 1];
  			if(!last.tr){
  				break;
  			}
  			prev = last;
  		}
  	}else{
  		// if there is no preceding sibling, use the direct parent
  		prev = parent;
  	}
  	return prev;
  }
  
  
  $.ui.fancytree.registerExtension({
  	name: "table",
  	version: "0.2.1",
  	// Default options for this extension.
  	options: {
  		checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx)
  		customStatus: false,	 // true: generate renderColumns events for status nodes
  		indentation: 16,         // indent every node level by 16px
  		nodeColumnIdx: 0         // render node expander, icon, and title to this column (default: #0)
  	},
  	// Overide virtual methods for this extension.
  	// `this`       : is this extension object
  	// `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
  	treeInit: function(ctx){
  		var i, $row, tdRole,
  			tree = ctx.tree,
  			$table = tree.widget.element;
  
  		$table.addClass("fancytree-container fancytree-ext-table");
  		tree.tbody = $table.find("> tbody")[0];
  		tree.columnCount = $("thead >tr >th", $table).length;
  		$(tree.tbody).empty();
  
  		tree.rowFragment = document.createDocumentFragment();
  		$row = $("<tr />");
  		tdRole = "";
  		if(ctx.options.aria){
  			$row.attr("role", "row");
  			tdRole = " role='gridcell'";
  		}
  		for(i=0; i<tree.columnCount; i++) {
  			if(ctx.options.table.nodeColumnIdx === i){
  				$row.append("<td" + tdRole + "><span class='fancytree-node' /></td>");
  			}else{
  				$row.append("<td" + tdRole + " />");
  			}
  		}
  		tree.rowFragment.appendChild($row.get(0));
  
  		// Make sure that status classes are set on the node's <tr> elements
  		tree.statusClassPropName = "tr";
  		tree.ariaPropName = "tr";
  		this.nodeContainerAttrName = "tr";
  
  		// #489: make sure $container is set to <table>, even if ext-dnd is listed before ext-table
  		tree.$container = $table;
  
  		this._superApply(arguments);
  
  		// standard Fancytree created a root UL
  		$(tree.rootNode.ul).remove();
  		tree.rootNode.ul = null;
  //		tree.$container = $table;
  		// Add container to the TAB chain
  		this.$container.attr("tabindex", this.options.tabbable ? "0" : "-1");
  		if(this.options.aria){
  			tree.$container
  				.attr("role", "treegrid")
  				.attr("aria-readonly", true);
  		}
  	},
  	/* Called by nodeRender to sync node order with tag order.*/
  //    nodeFixOrder: function(ctx) {
  //    },
  	nodeRemoveChildMarkup: function(ctx) {
  		var node = ctx.node;
  //		node.debug("nodeRemoveChildMarkup()");
  		node.visit(function(n){
  			if(n.tr){
  				$(n.tr).remove();
  				n.tr = null;
  			}
  		});
  	},
  	nodeRemoveMarkup: function(ctx) {
  		var node = ctx.node;
  //		node.debug("nodeRemoveMarkup()");
  		if(node.tr){
  			$(node.tr).remove();
  			node.tr = null;
  		}
  		this.nodeRemoveChildMarkup(ctx);
  	},
  	/* Override standard render. */
  	nodeRender: function(ctx, force, deep, collapsed, _recursive) {
  		var children, firstTr, i, l, newRow, prevNode, prevTr, subCtx,
  			tree = ctx.tree,
  			node = ctx.node,
  			opts = ctx.options,
  			isRootNode = !node.parent;
  
  		if( !_recursive ){
  			ctx.hasCollapsedParents = node.parent && !node.parent.expanded;
  		}
  		// $.ui.fancytree.debug("*** nodeRender " + node + ", isRoot=" + isRootNode, "tr=" + node.tr, "hcp=" + ctx.hasCollapsedParents, "parent.tr=" + (node.parent && node.parent.tr));
  		if( !isRootNode ){
  			if(!node.tr){
  				if( ctx.hasCollapsedParents && !deep ) {
  					// #166: we assume that the parent will be (recursively) rendered
  					// later anyway.
  					node.debug("nodeRender ignored due to unrendered parent");
  					return;
  				}
  				// Create new <tr> after previous row
  				newRow = tree.rowFragment.firstChild.cloneNode(true);
  				prevNode = findPrevRowNode(node);
  				// $.ui.fancytree.debug("*** nodeRender " + node + ": prev: " + prevNode.key);
  				_assert(prevNode);
  				if(collapsed === true && _recursive){
  					// hide all child rows, so we can use an animation to show it later
  					newRow.style.display = "none";
  				}else if(deep && ctx.hasCollapsedParents){
  					// also hide this row if deep === true but any parent is collapsed
  					newRow.style.display = "none";
  //					newRow.style.color = "red";
  				}
  				if(!prevNode.tr){
  					_assert(!prevNode.parent, "prev. row must have a tr, or is system root");
  					tree.tbody.appendChild(newRow);
  				}else{
  					insertSiblingAfter(prevNode.tr, newRow);
  				}
  				node.tr = newRow;
  				if( node.key && opts.generateIds ){
  					node.tr.id = opts.idPrefix + node.key;
  				}
  				node.tr.ftnode = node;
  				if(opts.aria){
  					// TODO: why doesn't this work:
  //                  node.li.role = "treeitem";
  					$(node.tr).attr("aria-labelledby", "ftal_" + node.key);
  				}
  				node.span = $("span.fancytree-node", node.tr).get(0);
  				// Set icon, link, and title (normally this is only required on initial render)
  				this.nodeRenderTitle(ctx);
  				// Allow tweaking, binding, after node was created for the first time
  //				tree._triggerNodeEvent("createNode", ctx);
  				if ( opts.createNode ){
  					opts.createNode.call(tree, {type: "createNode"}, ctx);
  				}
  			} else {
  				if( force ) {
  					// Set icon, link, and title (normally this is only required on initial render)
  					this.nodeRenderTitle(ctx); // triggers renderColumns()
  				} else {
  					// Update element classes according to node state
  					this.nodeRenderStatus(ctx);
  				}
  			}
  		}
  		// Allow tweaking after node state was rendered
  //		tree._triggerNodeEvent("renderNode", ctx);
  		if ( opts.renderNode ){
  			opts.renderNode.call(tree, {type: "renderNode"}, ctx);
  		}
  		// Visit child nodes
  		// Add child markup
  		children = node.children;
  		if(children && (isRootNode || deep || node.expanded)){
  			for(i=0, l=children.length; i<l; i++) {
  				subCtx = $.extend({}, ctx, {node: children[i]});
  				subCtx.hasCollapsedParents = subCtx.hasCollapsedParents || !node.expanded;
  				this.nodeRender(subCtx, force, deep, collapsed, true);
  			}
  		}
  		// Make sure, that <tr> order matches node.children order.
  		if(children && !_recursive){ // we only have to do it once, for the root branch
  			prevTr = node.tr || null;
  			firstTr = tree.tbody.firstChild;
  			// Iterate over all descendants
  			node.visit(function(n){
  				if(n.tr){
  					if(!n.parent.expanded && n.tr.style.display !== "none"){
  						// fix after a node was dropped over a collapsed
  						n.tr.style.display = "none";
  						setChildRowVisibility(n, false);
  					}
  					if(n.tr.previousSibling !== prevTr){
  						node.debug("_fixOrder: mismatch at node: " + n);
  						var nextTr = prevTr ? prevTr.nextSibling : firstTr;
  						tree.tbody.insertBefore(n.tr, nextTr);
  					}
  					prevTr = n.tr;
  				}
  			});
  		}
  		// Update element classes according to node state
  		// if(!isRootNode){
  		// 	this.nodeRenderStatus(ctx);
  		// }
  	},
  	nodeRenderTitle: function(ctx, title) {
  		var $cb,
  			node = ctx.node,
  			opts = ctx.options;
  
  		this._superApply(arguments);
  		// Move checkbox to custom column
  		if(opts.checkbox && opts.table.checkboxColumnIdx != null ){
  			$cb = $("span.fancytree-checkbox", node.span).detach();
  			$(node.tr).find("td").eq(+opts.table.checkboxColumnIdx).html($cb);
  		}
  		// Update element classes according to node state
  		if( ! node.isRoot() ){
  			this.nodeRenderStatus(ctx);
  		}
  		if( !opts.table.customStatus && node.isStatusNode() ) {
  			// default rendering for status node: leave other cells empty
  		} else if ( opts.renderColumns ) {
  			// Let user code write column content
  			opts.renderColumns.call(ctx.tree, {type: "renderColumns"}, ctx);
  		}
  	},
  	nodeRenderStatus: function(ctx) {
  		var indent,
  			node = ctx.node,
  			opts = ctx.options;
  
  		this._superApply(arguments);
  
  		$(node.tr).removeClass("fancytree-node");
  		// indent
  		indent = (node.getLevel() - 1) * opts.table.indentation;
  		$(node.span).css({paddingLeft: indent + "px"});  // #460
  		// $(node.span).css({marginLeft: indent + "px"});
  	 },
  	/* Expand node, return Deferred.promise. */
  	nodeSetExpanded: function(ctx, flag, opts) {
  		// flag defaults to true
  		flag = (flag !== false);
  
  		if((ctx.node.expanded && flag) || (!ctx.node.expanded && !flag)) {
  			// Expanded state isn't changed - just call base implementation
  			return this._superApply(arguments);
  		}
  
  		var dfd = new $.Deferred(),
  			subOpts = $.extend({}, opts, {noEvents: true, noAnimation: true});
  
  		opts = opts || {};
  
  		function _afterExpand(ok) {
  			setChildRowVisibility(ctx.node, flag);
  			if( ok ) {
  				if( flag && ctx.options.autoScroll && !opts.noAnimation && ctx.node.hasChildren() ) {
  					// Scroll down to last child, but keep current node visible
  					ctx.node.getLastChild().scrollIntoView(true, {topNode: ctx.node}).always(function(){
  						if( !opts.noEvents ) {
  							ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  						}
  						dfd.resolveWith(ctx.node);
  					});
  				} else {
  					if( !opts.noEvents ) {
  						ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  					}
  					dfd.resolveWith(ctx.node);
  				}
  			} else {
  				if( !opts.noEvents ) {
  					ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
  				}
  				dfd.rejectWith(ctx.node);
  			}
  		}
  		// Call base-expand with disabled  events and animation
  		this._super(ctx, flag, subOpts).done(function () {
  			_afterExpand(true);
  		}).fail(function () {
  			_afterExpand(false);
  		});
  		return dfd.promise();
  	},
  	nodeSetStatus: function(ctx, status, message, details) {
  		if(status === "ok"){
  			var node = ctx.node,
  				firstChild = ( node.children ? node.children[0] : null );
  			if ( firstChild && firstChild.isStatusNode() ) {
  				$(firstChild.tr).remove();
  			}
  		}
  		return this._superApply(arguments);
  	},
  	treeClear: function(ctx) {
  		this.nodeRemoveChildMarkup(this._makeHookContext(this.rootNode));
  		return this._superApply(arguments);
  	},
  	treeDestroy: function(ctx) {
  		this.$container.find("tbody").empty();
  		this.$source && this.$source.removeClass("ui-helper-hidden");
  	}
  	/*,
  	treeSetFocus: function(ctx, flag) {
  //	        alert("treeSetFocus" + ctx.tree.$container);
  		ctx.tree.$container.focus();
  		$.ui.fancytree.focusTree = ctx.tree;
  	}*/
  });
  }(jQuery, window, document));
  