mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-11-03 04:40:10 +00:00 
			
		
		
		
	split out the browser widget and started testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									17b75ed30c
								
							
						
					
					
						commit
						a620e60af5
					
				@ -122,6 +122,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script src="../ext-lib/require.js"></script>
 | 
					<script src="../ext-lib/require.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script src="browse-dialog.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var TREE = {
 | 
					var TREE = {
 | 
				
			||||||
@ -399,317 +402,6 @@ var KB = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//---
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// XXX NOTE: the widget itself does not need a title, that's the job for
 | 
					 | 
				
			||||||
//		a container widget (dialog, field, ...)
 | 
					 | 
				
			||||||
//		...it can be implemented trivially via an attribute and a :before
 | 
					 | 
				
			||||||
//		CSS class...
 | 
					 | 
				
			||||||
var BrowserClassPrototype = {
 | 
					 | 
				
			||||||
	// construct the dom...
 | 
					 | 
				
			||||||
	make: function(options){
 | 
					 | 
				
			||||||
		var browser = $('<div>')
 | 
					 | 
				
			||||||
			.addClass('browse')
 | 
					 | 
				
			||||||
			// make thie widget focusable...
 | 
					 | 
				
			||||||
			// NOTE: tabindex 0 means automatic tab indexing and -1 means 
 | 
					 | 
				
			||||||
			//		focusable bot not tabable...
 | 
					 | 
				
			||||||
			//.attr('tabindex', -1)
 | 
					 | 
				
			||||||
			.attr('tabindex', 0)
 | 
					 | 
				
			||||||
			// focus the widget if something inside is clicked...
 | 
					 | 
				
			||||||
			.click(function(){
 | 
					 | 
				
			||||||
				$(this).focus()
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if(options.path == null || options.show_path){
 | 
					 | 
				
			||||||
			browser
 | 
					 | 
				
			||||||
				.append($('<div>')
 | 
					 | 
				
			||||||
					   .addClass('v-block path'))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		browser
 | 
					 | 
				
			||||||
			.append($('<div>')
 | 
					 | 
				
			||||||
				   .addClass('v-block list'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return browser
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// XXX Q: should we make a base list dialog and build this on that or
 | 
					 | 
				
			||||||
//		simplify this to implement a list (removing the path and disbling
 | 
					 | 
				
			||||||
//		traversal)??
 | 
					 | 
				
			||||||
// XXX need a search/filter field...
 | 
					 | 
				
			||||||
// XXX need base events:
 | 
					 | 
				
			||||||
//		- opne
 | 
					 | 
				
			||||||
//		- update
 | 
					 | 
				
			||||||
//		- select (???)
 | 
					 | 
				
			||||||
var BrowserPrototype = {
 | 
					 | 
				
			||||||
	dom: null,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	options: {
 | 
					 | 
				
			||||||
		//path: null,
 | 
					 | 
				
			||||||
		//show_path: null,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// XXX this should prevent event handler deligation...
 | 
					 | 
				
			||||||
	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...
 | 
					 | 
				
			||||||
	// XXX does the path includes the currently selected element?
 | 
					 | 
				
			||||||
	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...
 | 
					 | 
				
			||||||
	// XXX trigger an "update" event...
 | 
					 | 
				
			||||||
	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($('<div>')
 | 
					 | 
				
			||||||
				.addClass('dir')
 | 
					 | 
				
			||||||
				.click(popDir)
 | 
					 | 
				
			||||||
				.text(e))
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// fill the children list...
 | 
					 | 
				
			||||||
		this.list(path)
 | 
					 | 
				
			||||||
			.forEach(function(e){
 | 
					 | 
				
			||||||
				l.append($('<div>')
 | 
					 | 
				
			||||||
					.click(pushDir)
 | 
					 | 
				
			||||||
					.text(e))
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		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(<number>)
 | 
					 | 
				
			||||||
	//		-> elem
 | 
					 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	//	Select element by its text...
 | 
					 | 
				
			||||||
	//	.select('"<text>"')
 | 
					 | 
				
			||||||
	//		-> elem
 | 
					 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	//	.select(<elem>)
 | 
					 | 
				
			||||||
	//		-> 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'){
 | 
					 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		var path = this.path.push(elem.text())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// if not traversable call the action...
 | 
					 | 
				
			||||||
		if(this.isTraversable && ! this.isTraversable(path)){
 | 
					 | 
				
			||||||
			return this.action(path)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this
 | 
					 | 
				
			||||||
			.update(path)
 | 
					 | 
				
			||||||
			.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...
 | 
					 | 
				
			||||||
	// 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(){ },
 | 
					 | 
				
			||||||
	list: function(){ },
 | 
					 | 
				
			||||||
	isTraversable: null,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// XXX need to get a container....
 | 
					 | 
				
			||||||
	// XXX prepare/merge options...
 | 
					 | 
				
			||||||
	__init__: function(parent, options){
 | 
					 | 
				
			||||||
		// XXX merge options...
 | 
					 | 
				
			||||||
		// XXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
var Browser = 
 | 
					 | 
				
			||||||
//module.Browser = 
 | 
					 | 
				
			||||||
object.makeConstructor('Browser', 
 | 
					 | 
				
			||||||
		BrowserClassPrototype, 
 | 
					 | 
				
			||||||
		BrowserPrototype)
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//---
 | 
					//---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -725,20 +417,45 @@ requirejs(['../lib/keyboard', '../object'], function(k, o){
 | 
				
			|||||||
				KB,
 | 
									KB,
 | 
				
			||||||
				function(k){ window.DEBUG && console.log(k) }))
 | 
									function(k){ window.DEBUG && console.log(k) }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Browser = object.makeConstructor('Browser', 
 | 
				
			||||||
 | 
								BrowserClassPrototype, 
 | 
				
			||||||
 | 
								BrowserPrototype)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b = Browser($('.container').last(), {
 | 
				
			||||||
 | 
								list: function(path){
 | 
				
			||||||
 | 
									var cur = TREE
 | 
				
			||||||
 | 
									path.forEach(function(p){
 | 
				
			||||||
 | 
										cur = cur[p]
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return Object.keys(cur)
 | 
				
			||||||
 | 
										.filter(function(k){
 | 
				
			||||||
 | 
											// XXX stub...
 | 
				
			||||||
 | 
											return typeof(cur[k]) != typeof('str')
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.focus()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(function(){
 | 
					$(function(){
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
	var browser = make()
 | 
						var browser = make()
 | 
				
			||||||
	$('.container')
 | 
						$('.container').first()
 | 
				
			||||||
		.empty()
 | 
							.empty()
 | 
				
			||||||
		.append(browser)
 | 
							.append(browser)
 | 
				
			||||||
	showPath(browser, '/', TREE)
 | 
						showPath(browser, '/', TREE)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$('.container').first().remove()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	$('.container').draggable({ 
 | 
						$('.container').draggable({ 
 | 
				
			||||||
			cancel: ".path .dir, .list div" 
 | 
								cancel: ".path .dir, .list div" 
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	browser.focus()
 | 
						//browser.focus()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
@ -796,6 +513,8 @@ $(function(){
 | 
				
			|||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="container">
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										346
									
								
								ui (gen4)/experiments/browse-dialog.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										346
									
								
								ui (gen4)/experiments/browse-dialog.js
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,346 @@
 | 
				
			|||||||
 | 
					/**********************************************************************
 | 
				
			||||||
 | 
					* 
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					**********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//var DEBUG = DEBUG != null ? DEBUG : true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XXX NOTE: the widget itself does not need a title, that's the job for
 | 
				
			||||||
 | 
					//		a container widget (dialog, field, ...)
 | 
				
			||||||
 | 
					//		...it can be implemented trivially via an attribute and a :before
 | 
				
			||||||
 | 
					//		CSS class...
 | 
				
			||||||
 | 
					var BrowserClassPrototype = {
 | 
				
			||||||
 | 
						// construct the dom...
 | 
				
			||||||
 | 
						make: function(options){
 | 
				
			||||||
 | 
							var browser = $('<div>')
 | 
				
			||||||
 | 
								.addClass('browse')
 | 
				
			||||||
 | 
								// make thie widget focusable...
 | 
				
			||||||
 | 
								// NOTE: tabindex 0 means automatic tab indexing and -1 means 
 | 
				
			||||||
 | 
								//		focusable bot not tabable...
 | 
				
			||||||
 | 
								//.attr('tabindex', -1)
 | 
				
			||||||
 | 
								.attr('tabindex', 0)
 | 
				
			||||||
 | 
								// focus the widget if something inside is clicked...
 | 
				
			||||||
 | 
								.click(function(){
 | 
				
			||||||
 | 
									$(this).focus()
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if(options.path == null || options.show_path){
 | 
				
			||||||
 | 
								browser
 | 
				
			||||||
 | 
									.append($('<div>')
 | 
				
			||||||
 | 
										   .addClass('v-block path'))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							browser
 | 
				
			||||||
 | 
								.append($('<div>')
 | 
				
			||||||
 | 
									   .addClass('v-block list'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return browser
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XXX Q: should we make a base list dialog and build this on that or
 | 
				
			||||||
 | 
					//		simplify this to implement a list (removing the path and disbling
 | 
				
			||||||
 | 
					//		traversal)??
 | 
				
			||||||
 | 
					// XXX need a search/filter field...
 | 
				
			||||||
 | 
					// XXX need base events:
 | 
				
			||||||
 | 
					//		- opne
 | 
				
			||||||
 | 
					//		- update
 | 
				
			||||||
 | 
					//		- select (???)
 | 
				
			||||||
 | 
					// XXX add "current selection" to the path...
 | 
				
			||||||
 | 
					var BrowserPrototype = {
 | 
				
			||||||
 | 
						dom: null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						options: {
 | 
				
			||||||
 | 
							//path: null,
 | 
				
			||||||
 | 
							//show_path: null,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// XXX this should prevent event handler deligation...
 | 
				
			||||||
 | 
						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...
 | 
				
			||||||
 | 
						// XXX does the path includes the currently selected element?
 | 
				
			||||||
 | 
						get path(){
 | 
				
			||||||
 | 
							var skip = false
 | 
				
			||||||
 | 
							return this.dom.find('.path .dir')
 | 
				
			||||||
 | 
								.map(function(i, e){ return $(e).text() })
 | 
				
			||||||
 | 
								.toArray()
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						set path(value){
 | 
				
			||||||
 | 
							console.log('!!!', value)
 | 
				
			||||||
 | 
							// XXX normalize path...
 | 
				
			||||||
 | 
							return this.update(value)
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// update path...
 | 
				
			||||||
 | 
						// XXX trigger an "update" event...
 | 
				
			||||||
 | 
						update: function(path){
 | 
				
			||||||
 | 
							var browser = this.dom
 | 
				
			||||||
 | 
							var that = this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var p = browser.find('.path').empty()
 | 
				
			||||||
 | 
							var l = browser.find('.list').empty()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// fill the path field...
 | 
				
			||||||
 | 
							path.forEach(function(e){
 | 
				
			||||||
 | 
								p.append($('<div>')
 | 
				
			||||||
 | 
									.addClass('dir')
 | 
				
			||||||
 | 
									.click(function(){ that.pop() })
 | 
				
			||||||
 | 
									.text(e))
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// fill the children list...
 | 
				
			||||||
 | 
							this.list(path)
 | 
				
			||||||
 | 
								.forEach(function(e){
 | 
				
			||||||
 | 
									l.append($('<div>')
 | 
				
			||||||
 | 
										.click(function(){ that.push() })
 | 
				
			||||||
 | 
										.text(e))
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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(<number>)
 | 
				
			||||||
 | 
						//		-> elem
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//	Select element by its text...
 | 
				
			||||||
 | 
						//	.select('"<text>"')
 | 
				
			||||||
 | 
						//		-> elem
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//	.select(<elem>)
 | 
				
			||||||
 | 
						//		-> 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'){
 | 
				
			||||||
 | 
								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')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 thsi
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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) : 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 :                                                */
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user