')
+				   .addClass('v-block actions'))
+		return browser
+	},
+}
+
+var BrowserPrototype = {
+	dom: null,
+
+	keyboard: {
+		'.browse':{
+			Up: 'prev',
+			Backspace: 'Up',
+			Down: 'next',
+			Left: 'pop',
+			Right: 'push',
+
+			Enter: 'action',
+			Esc: 'close',
+		},
+	},
+
+	// base api...
+	// NOTE: to avoid duplicating and syncing data, the actual path is 
+	//		stored in DOM...
+	get path(){
+		var skip = false
+		return this.dom.find('.path .dir')
+			.map(function(i, e){ return $(e).text() })
+			.toArray()
+	},
+	set path(value){
+		// XXX normalize path...
+		return this.update(path)
+	},
+
+	// update path...
+	update: function(path){
+		var browser = this.dom
+
+		var p = browser.find('.path').empty()
+		var l = browser.find('.list').empty()
+
+		// fill the path field...
+		path.forEach(function(e){
+			p.append($('
')
+				.addClass('dir')
+				.click(popDir)
+				.text(e))
+		})
+
+		// fill the children list...
+		this.list(path)
+			.forEach(function(e){
+				l.append($('
')
+					.click(pushDir)
+					.text(e))
+			})
+
+		return this
+	},
+
+	// internal actions...
+
+	//
+	//	Select first/last child
+	//	.select('first')
+	//	.select('last')
+	//		-> elem
+	//
+	//	Select previous/lext child
+	//	.select('prev')
+	//	.select('next')
+	//		-> elem
+	//
+	//	Deselect
+	//	.select('none')
+	//		-> elem
+	//
+	//	Get selected element if it exists, null otherwise...
+	//	.select('!')
+	//		-> elem
+	//		-> $()
+	//
+	//	Select element by sequence number
+	//	.select()
+	//		-> elem
+	//
+	//	Select element by its text...
+	//	.select('""')
+	//		-> elem
+	//
+	//	.select()
+	//		-> elem
+	//
+	// This will return a jQuery object.
+	//
+	//
+	// XXX revise return values...
+	select: function(elem){
+		var browser = this.dom
+		var elems = browser.find('.list div')
+
+		if(elems.length == 0){
+			return $()
+		}
+
+		elem = elem || this.select('!')
+		// if none selected get the first...
+		elem = elem.length == 0 ? 'first' : elem
+
+		// first/last...
+		if(elem == 'first' || elem == 'last'){
+			return this.select(elems[elem]())
+		
+		// prev/next...
+		} else if(elem == 'prev' || elem == 'next'){
+			var to = this.select('!', browser)[elem]('.list div')
+			if(to.length == 0){
+				return this.select(elem == 'prev' ? 'last' : 'first', browser)
+			}
+			this.select('none')
+			return this.select(to)
+
+		// deselect...
+		} else if(elem == 'none'){
+			return elems
+				.filter('.selected')
+				.removeClass('selected')
+
+		// strict...
+		} else if(elem == '!'){
+			return elems.filter('.selected')
+
+		// number...
+		} else if(typeof(elem) == typeof(123)){
+			return this.select($(elems[elem]))
+
+		// string...
+		} else if(typeof(elem) == typeof('str') 
+				&& /^'.*'$|^".*"$/.test(elem.trim())){
+			elem = elem.trim().slice(1, -1)
+			return this.select(browser.find('.list div')
+					.filter(function(i, e){
+						return $(e).text() == elem
+					}))
+
+		// element...
+		} else {
+			this.select('none')
+			return elem.addClass('selected')
+		}
+	},
+
+	// XXX check if we need to do the ,action when the element id not traversable...
+	push: function(elem){
+		var browser = this.dom 
+		var elem = this.select(elem || '!')
+
+		// nothing selected, select first and exit...
+		if(elem.length == 0){
+			this.select()
+			return this
+		}
+
+
+		this
+			.update(this.path.push(elem.text()))
+			.select()
+
+		return this
+	},
+	// pop an element off the path / go up one level...
+	pop: function(){
+		var browser = this.dom
+		var path = this.path
+		var dir = path.pop()
+
+		this.update(path)
+
+		this.select('"'+dir+'"')
+
+		return this
+	},
+	next: function(elem){
+		if(elem != null){
+			this.select(elem)
+		}
+		this.select('next')
+		return this
+	},
+	prev: function(elem){
+		if(elem != null){
+			this.select(elem)
+		}
+		this.select('prev')
+		return this
+	},
+
+	// XXX think about the API...
+	action: function(){
+		var res = this.open(this.path)
+
+		// XXX close the dialog if everything is OK...
+		// XXX
+
+		return res
+	},
+	// close the dialog...
+	// XXX
+	close: function(){
+	},
+
+	// extension methods...
+	// XXX think about the API...
+	//		needs to control closing of the dialog
+	open: function(){
+	},
+	list: function(){
+	},
+	isTraversable: null,
+
+	// XXX
+	__init__: function(parent){
+		// build the dom...
+		var dom = this.dom = this.constructor.make()
+
+		// add keyboard handler...
+		dom.keydown(
+			keyboard.makeKeyboardHandler(
+				this.keyboard,
+				function(k){ window.DEBUG && console.log(k) },
+				this))
+
+		// attach to parent...
+		if(parent != null){
+			parent.append(dom)
+		}
+	},
+}
+
+
+/*
+var Browser = 
+//module.Browser = 
+object.makeConstructor('Browser', 
+		BrowserClassPrototype, 
+		BrowserPrototype)
+*/
+
+
+
+//---
+
+
+requirejs(['../lib/keyboard', '../object'], function(k, o){
+	keyboard = k
+	object = o
 
 	// setup base keyboard for devel, in case something breaks...
 	//$(document)