Sidebar nav: add click-to-expand behavior on folders
- Refactor nav renderer to be customizeable
- JSdoc renderer and explorer
diff --git a/bundles/org.eclipse.orion.client.ui/web/css/ide.css b/bundles/org.eclipse.orion.client.ui/web/css/ide.css
index 5bef312..97108d8 100644
--- a/bundles/org.eclipse.orion.client.ui/web/css/ide.css
+++ b/bundles/org.eclipse.orion.client.ui/web/css/ide.css
@@ -43,7 +43,11 @@
 	padding: 2px;
 }
 
-.navlinkonpage {
+.nav_expandinplace:hover {
+	cursor: pointer;
+}
+
+.navlinkonpage, .nav_expandinplace {
 	text-decoration: none;
 	padding: 2px;
 }
diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/explorer-table.js b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/explorer-table.js
index 4aafc06..f72c266 100644
--- a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/explorer-table.js
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/explorer-table.js
@@ -310,11 +310,19 @@
 		var rowId = this.model.getId(item);
 		return this.renderer.tableTree.isExpanded(rowId);
 	};
-		
+
+	/**
+	 * Returns the node that a rename text input box should appear over top of.
+	 * @name orion.explorers.FileExplorer#getNameNode
+	 * @function
+	 * @param {Object} item Item being renamed
+	 * @returns {Element}
+	 */
 	FileExplorer.prototype.getNameNode = function(item) {
 		var rowId = this.model.getId(item);
 		if (rowId) {
 			// I know this from my renderer below.
+			// TODO This approach fails utterly for a custom renderer, better hope they override this method.
 			return lib.node(rowId+"NameLink"); //$NON-NLS-0$
 		}
 	};
diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigationUtils.js b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigationUtils.js
index 127c612..8466384 100644
--- a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigationUtils.js
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigationUtils.js
@@ -18,8 +18,8 @@
 	 * Generate a grid navigation item into a given array. A grid navigation item is presented by a wrapper object wrapping the domNode. 
 	 *
 	 * @param {Array} domNodeWrapperList the array that holds the grid navigation item. Normally the .gridChildren property from a row model.
-	 * @param {DomNode} domNode the html dom node representing a grid. Normally left or right arrow keys on the current row highlight the dom node.
-	 *        When a grid is rendered, the caller has to decide what dom node can be passed. 
+	 * @param {Element} element the html dom element representing a grid. Normally left or right arrow keys on the current row highlight the dom element.
+	 *        When a grid is rendered, the caller has to decide what dom element can be passed. 
 	 */
 	 
 	 
@@ -41,8 +41,8 @@
 	 *
 	 * @param {ExplorerNavDict} navDict the dictionary that holds the info of all navigation info from model id.
 	 * @param {object} rowModel the row model from the {treeModelIterator}.
-	 * @param {DomNode} domNode the html dom node representing a grid. Normally left or right arrow keys on the current row highlight the dom node.
-	 *        When a grid is rendered, the caller has to decide what dom node can be passed. 
+	 * @param {Element} element the html dom element representing a grid. Normally left or right arrow keys on the current row highlight the dom element.
+	 *        When a grid is rendered, the caller has to decide what dom element can be passed. 
 	 */
 	function addNavGrid(navDict, rowModel, domNode) {
 		if(!navDict){
diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigatorRenderer.js b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigatorRenderer.js
index 1bbfec8..8ddc64a 100644
--- a/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigatorRenderer.js
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/explorers/navigatorRenderer.js
@@ -75,12 +75,11 @@
 	 * @param {Object} [defaultEditor] will be computed if not provided, but subject to the same caveat as openWithCommands.

 	 * @param {Object} [linkProperties] gives additional properties to mix in to the HTML anchor element.

 	 */

-	function createLink(folderPageURL, item, idPrefix, commandService, contentTypeService, /* optional */ openWithCommands, /* optional */defaultEditor, /* optional */ linkProperties) {

+	function createLink(folderPageURL, item, commandService, contentTypeService, /* optional */ openWithCommands, /* optional */defaultEditor, /* optional */ linkProperties) {

 		var link;

 		if (item.Directory) {

 			link = document.createElement("a"); //$NON-NLS-0$

 			link.className = "navlinkonpage"; //$NON-NLS-0$

-			link.id = idPrefix+"NameLink"; //$NON-NLS-0$

 			link.href = folderPageURL + "#" + item.ChildrenLocation; //$NON-NLS-0$

 			link.appendChild(document.createTextNode(item.Name));

 		} else {

@@ -99,7 +98,6 @@
 			}

 			link = document.createElement("a"); //$NON-NLS-0$

 			link.className= "navlink targetSelector"; //$NON-NLS-0$

-			link.id = idPrefix+"NameLink"; //$NON-NLS-0$

 			if (linkProperties && typeof linkProperties === "object") { //$NON-NLS-0$

 				Object.keys(linkProperties).forEach(function(property) {

 					link[property] = linkProperties[property];

@@ -149,7 +147,7 @@
 	NavigatorRenderer.prototype = new mExplorer.SelectionRenderer(); 

 	

 	/**

-	 * we are really only using the header for a spacer at this point.

+	 * Creates the column header element. We are really only using the header for a spacer at this point.

 	 * @name orion.explorer.NavigatorRenderer.prototype.getCellHeaderElement

 	 * @function

 	 * @returns {Element}

@@ -180,43 +178,66 @@
 	};

 

 	/**

+	* Subclasses can override this function to customize the DOM Element that is created to represent a folder.

+	 * The default implementation creates either a hyperlink or a plain text node.

+	 * @name orion.explorer.NavigatorRenderer#createFolderNode

+	 * @type {Function}

+	 * @see #showFolderLinks

+	 * @see #folderLink

+	 * @param {Object} folder The folder to create a node for.

+	 * @returns {Element} The folder element.

+	 */

+	// The returned element must have an <code>id</code> property.

+	NavigatorRenderer.prototype.createFolderNode = function(folder, idPrefix) {

+		var itemNode;

+		if (this.showFolderLinks) { //$NON-NLS-0$

+			// TODO see https://bugs.eclipse.org/bugs/show_bug.cgi?id=400121

+			itemNode = createLink(this.folderLink || "", folder, this.commandService, this.contentTypeService); //$NON-NLS-0$

+		} else {

+			itemNode = document.createElement("span"); //$NON-NLS-0$

+			itemNode.textContent = folder.Name;

+		}

+		return itemNode;

+	};

+	/**

+	 * Whether the default implementation of {@link #createFolderNode} should show folders should as links (<code>true</code>),

+	 * or just plain text (<code>false</code>).

 	 * @name orion.explorer.NavigatorRenderer#showFolderLinks

 	 * @type {Boolean}

-	 * @description Whether folders should be links (<code>true</code>), or just plain text (<code>false</code>). Default is to show folders as links.

+	 * @default true

 	 */

 	NavigatorRenderer.prototype.showFolderLinks = true;

 	/**

+	 * Gives the base href to be used by the default implementation of {@link #createFolderNode} for creating folder links.

+	 * This property only takes effect if {@link #showFolderLinks} is <code>true</code>. 

+	 * TODO see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=400121">Bug 400121</a>

 	 * @name orion.explorer.NavigatorRenderer#folderLink

 	 * @type {String}

-	 * @description Base link URL to use on folder text. Only applies if {@link #showFolderLinks} is true. TODO see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=400121">Bug 400121</a>

+	 * @default ""

 	 */

 	/**

+	 * Generate the DOM element for a cell. If you override this function, you will most likely have to override {@link orion.explorers.FileExplorer#getNameNode}

+	 * in your explorer class.

 	 * @name orion.explorer.NavigatorRenderer#getCellElement

 	 * @function

 	 * @returns {Element}

 	 */

 	NavigatorRenderer.prototype.getCellElement = function(col_no, item, tableRow){

 		switch(col_no){

-

 		case 0:

 			var col = document.createElement('td'); //$NON-NLS-0$

 			var span = document.createElement("span"); //$NON-NLS-0$

 			span.id = tableRow.id+"MainCol"; //$NON-NLS-0$

 			col.appendChild(span);

 			span.className = "mainNavColumn"; //$NON-NLS-0$

-			var link;

+			var itemNode;

 			if (item.Directory) {

 				// defined in ExplorerRenderer.  Sets up the expand/collapse behavior

 				var image = this.getExpandImage(tableRow, span);

+				itemNode = this.createFolderNode(item);

 

-				if (this.showFolderLinks) { //$NON-NLS-0$

-					// TODO see https://bugs.eclipse.org/bugs/show_bug.cgi?id=400121

-					link = createLink(this.folderLink || "", item, tableRow.id, this.commandService, this.contentTypeService);

-					span.appendChild(link); //$NON-NLS-0$

-					this.explorer._makeDropTarget(item, link);

-				} else {

-					span.appendChild(document.createTextNode(item.Name));

-				}

+				span.appendChild(itemNode);

+				this.explorer._makeDropTarget(item, itemNode);

 				this.explorer._makeDropTarget(item, tableRow);

 			} else {

 				var i;			

@@ -230,11 +251,15 @@
 						}

 					}

 				}

-				link = createLink("", item, tableRow.id, this.commandService, this.contentTypeService, this.openWithCommands, this.defaultEditor, { target: this.target });

-				span.appendChild(link); //$NON-NLS-0$

+				itemNode = createLink("", item, this.commandService, this.contentTypeService, this.openWithCommands, this.defaultEditor, { target: this.target });

+				span.appendChild(itemNode); //$NON-NLS-0$

 			}

-			if (link) {

-				mNavUtils.addNavGrid(this.explorer.getNavDict(), item, link);

+			if (itemNode) {

+				// orion.explorers.FileExplorer#getNameNode

+				itemNode.id = tableRow.id + "NameLink"; //$NON-NLS-0$

+				if (itemNode.nodeType === 1) {

+					mNavUtils.addNavGrid(this.explorer.getNavDict(), item, itemNode);

+				}

 			}

 			// render any inline commands that are present.

 			if (this.actionScopeId) {

diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/navoutliner.js b/bundles/org.eclipse.orion.client.ui/web/orion/navoutliner.js
index 12ce563..a5842ec 100644
--- a/bundles/org.eclipse.orion.client.ui/web/orion/navoutliner.js
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/navoutliner.js
@@ -31,9 +31,9 @@
 		col.classList.add("mainNavColumn"); //$NON-NLS-0$
 		col.classList.add("singleNavColumn"); //$NON-NLS-0$
 		if (item.directory) {
-			link = mNavRenderer.createLink(require.toUrl("navigate/table.html"), { Name: item.name, ChildrenLocation: item.path, Directory: true }, "", this.commandService, this.contentTypeService); //$NON-NLS-0$
+			link = mNavRenderer.createLink(require.toUrl("navigate/table.html"), { Name: item.name, ChildrenLocation: item.path, Directory: true }, this.commandService, this.contentTypeService); //$NON-NLS-0$
 		} else if (item.path) {
-			link = mNavRenderer.createLink("", { Name: item.name, Location: item.path }, "", this.commandService, this.contentTypeService); //$NON-NLS-0$
+			link = mNavRenderer.createLink("", { Name: item.name, Location: item.path }, this.commandService, this.contentTypeService); //$NON-NLS-0$
 		} else if (typeof(item.getProperty) === "function" && item.getProperty("Name") && item.getProperty("top")) { //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
 			href = require.toUrl("navigate/table.html") + "#" + item.getProperty("top"); //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-0$
 			clazz = "navlinkonpage"; //$NON-NLS-0$
diff --git a/bundles/org.eclipse.orion.client.ui/web/orion/widgets/nav/mini-nav.js b/bundles/org.eclipse.orion.client.ui/web/orion/widgets/nav/mini-nav.js
index be2b0c9..4b07f31 100644
--- a/bundles/org.eclipse.orion.client.ui/web/orion/widgets/nav/mini-nav.js
+++ b/bundles/org.eclipse.orion.client.ui/web/orion/widgets/nav/mini-nav.js
@@ -133,8 +133,32 @@
 		NavigatorRenderer.apply(this, arguments);
 	}
 	MiniNavRenderer.prototype = Object.create(NavigatorRenderer.prototype);
-	MiniNavRenderer.prototype.showFolderLinks = true;
-//	MiniNavRenderer.prototype.folderLink = require.toUrl("navigate/table.html"); //$NON-NLS-0$
+	MiniNavRenderer.prototype.createFolderNode = function(folder) {
+		var node = NavigatorRenderer.prototype.createFolderNode.call(this, folder);
+		node.classList.add("nav_expandinplace"); //$NON-NLS-0$;
+		// TODO wasteful, should not need listener per node. should get model item from nav handler
+		node.addEventListener("click", this.onFolderClick.bind(this, folder)); //$NON-NLS-0$
+		return node;
+	};
+	MiniNavRenderer.prototype.onFolderClick = function(folder, evt) {
+		var navHandler = this.explorer.getNavHandler();
+		if (navHandler) {
+			navHandler.cursorOn(folder);
+			navHandler.setSelection(folder, false);
+			// now toggle its expand/collapse state
+			var curModel = navHandler._modelIterator.cursor();
+			if (navHandler.isExpandable(curModel)){
+				if(!navHandler.isExpanded(curModel)){
+					this.explorer.myTree.expand(curModel);
+				} else {
+					this.explorer.myTree.collapse(curModel);
+				}
+				evt.preventDefault();
+				return false;
+			}
+		}
+	};
+	MiniNavRenderer.prototype.showFolderLinks = false;
 	MiniNavRenderer.prototype.oneColumn = true;
 
 	/**