')
.addClass('dir')
.click(function(){
that
.update(cur.slice(0, -1))
.select('"'+cur.pop()+'"')
})
.text(e))
})
// add current selction indicator...
p.append($('
')
.addClass('dir cur')
// XXX start search/filter...
// - on click / letter set content editable
// - triger filterig on modified
// - disable nav in favor of editing
// - enter/blur to exit edit mode
// - esc to cancel and reset
// XXX add a filter mode...
.click(function(){
//that.update(path.concat($(this).text()))
$(this)
.text('')
.attr('contenteditable', true)
.keyup(function(){
that.filter($(this).text())
})
}))
// fill the children list...
this.list(path)
.forEach(function(e){
l.append($('
')
.click(function(){
that.update(that.path.concat([$(this).text()]))
})
.text(e))
})
return this
},
// XXX should have two non_matched modes:
// - hide - hide non-matching content
// - shadow - shadow non-matching content
// XXX pattern modes:
// - lazy match
// abc -> *abc* -> ^.*abc.*$
// ab cd -> *ab*cd* -> ^.*ab.*cd.*$
// - glob
// - regex
// XXX sort:
// - as-is
// - best match
filter: function(pattern, mode, non_matched, sort){
var that = this
var browser = this.dom
// show all...
if(pattern == null || pattern.trim() == '*'){
this.update()
// basic filter...
} else {
var l = browser.find('.list>div')
l.each(function(i, e){
e = $(e)
var t = e.text()
var i = t.search(pattern)
if(i < 0){
e.remove()
} else {
e.html(t.replace(pattern, pattern.bold()))
}
})
}
return this
},
// internal actions...
// Select a list element...
//
// 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...
// XXX Q: should this trigger a "select" event???
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'){
browser.find('.path .dir.cur').empty()
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')
browser.find('.path .dir.cur').text(elem.text())
return elem.addClass('selected')
}
},
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
}
var path = this.path
path.push(elem.text())
// if not traversable call the action...
if(this.isTraversable != null
&& (this.isTraversable !== false
|| ! this.isTraversable(path))){
return this.action(path)
}
this.path = path
this.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
},
focus: function(){
this.dom.focus()
return this
},
// XXX think about the API...
// XXX trigger an "open" event...
action: function(){
var elem = this.select('!')
// nothing selected, select first and exit...
if(elem.length == 0){
this.select()
return this
}
var path = this.path.push(elem.text())
var res = this.open(path)
return res
},
// extension methods...
open: function(path){
var m = this.options.list
return m ? m.call(this, path) : path
},
list: function(path){
var m = this.options.list
return m ? m.call(this, path) : []
},
isTraversable: null,
// XXX need to get a container....
// XXX prepare/merge options...
// XXX setup instance events...
__init__: function(parent, options){
// XXX merge options...
// XXX
this.options = options
// build the dom...
var dom = this.dom = this.constructor.make(options)
// add keyboard handler...
dom.keydown(
keyboard.makeKeyboardHandler(
this.keyboard,
// XXX
function(k){ window.DEBUG && console.log(k) },
this))
// attach to parent...
if(parent != null){
parent.append(dom)
}
// load the initial state...
this.update(this.path)
},
}
/*
var Browser =
//module.Browser =
object.makeConstructor('Browser',
BrowserClassPrototype,
BrowserPrototype)
*/
/**********************************************************************
* vim:set ts=4 sw=4 : */