')
+ .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)