| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //var DEBUG = DEBUG != null ? DEBUG : true
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 14:50:54 +03:00
										 |  |  | define(function(require){ var module = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var object = require('../object') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function proxyToDom(name){ | 
					
						
							|  |  |  | 	return function(){  | 
					
						
							|  |  |  | 		this.dom[name].apply(this.dom, arguments) | 
					
						
							|  |  |  | 		return this  | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | // NOTE: the widget itself does not need a title, that's the job for
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | //		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() | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		if(options.flat){ | 
					
						
							|  |  |  | 			browser.addClass('flat') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// path...
 | 
					
						
							|  |  |  | 		var path = $('<div>') | 
					
						
							|  |  |  | 			.addClass('v-block path') | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 			/* | 
					
						
							|  |  |  | 			.click(function(){ | 
					
						
							|  |  |  | 				// XXX set contenteditable...
 | 
					
						
							|  |  |  | 				// XXX set value to path...
 | 
					
						
							|  |  |  | 				// XXX select all...
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.on('blur', function(){ | 
					
						
							|  |  |  | 				// XXX unset contenteditable...
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.keyup(function(){ | 
					
						
							|  |  |  | 				// XXX update path...
 | 
					
						
							|  |  |  | 				// 		- set /../..../ to path
 | 
					
						
							|  |  |  | 				// 		- use the part after the last '/' ad filter...
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		  	*/ | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		if(options.show_path == false){ | 
					
						
							|  |  |  | 			path.hide() | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		browser | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 			.append(path) | 
					
						
							|  |  |  | 			// list...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			.append($('<div>') | 
					
						
							|  |  |  | 				   .addClass('v-block list')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return browser | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | // XXX need to handle long paths -- smart shortening or auto scroll...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | // XXX Q: should we make a base list dialog and build this on that or
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | //		simplify this to implement a list (removing the path and disabling
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | //		traversal)??
 | 
					
						
							|  |  |  | // XXX need base events:
 | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | //		- open
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | //		- update
 | 
					
						
							|  |  |  | //		- select (???)
 | 
					
						
							|  |  |  | var BrowserPrototype = { | 
					
						
							|  |  |  | 	dom: null, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-18 03:34:15 +03:00
										 |  |  | 	// option defaults and doc...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	options: { | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		// Initial path...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		//path: null,
 | 
					
						
							| 
									
										
										
										
											2015-05-18 03:34:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		//show_path: true,
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Enable/disable user selection filtering...
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 		// NOTE: this only affects .stopFilter(..)
 | 
					
						
							|  |  |  | 		filter: true, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		// If false will disable traversal...
 | 
					
						
							|  |  |  | 		// NOTE: if false this will also disable traversal up.
 | 
					
						
							|  |  |  | 		// NOTE: this will not disable manual updates or explicit path 
 | 
					
						
							|  |  |  | 		// 		setting.
 | 
					
						
							|  |  |  | 		// NOTE: another way to disable traversal is to set 
 | 
					
						
							|  |  |  | 		// 		.not-traversable on the .browse element
 | 
					
						
							|  |  |  | 		traversable: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Handle keys that are not bound...
 | 
					
						
							| 
									
										
										
										
											2015-05-18 03:34:15 +03:00
										 |  |  | 		// NOTE: to disable, set ot undefined.
 | 
					
						
							|  |  |  | 		logKeys: function(k){ window.DEBUG && console.log(k) }, | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// If set disables leading and trailing '/' on list and path 
 | 
					
						
							|  |  |  | 		// elements.
 | 
					
						
							|  |  |  | 		// This is mainly used for flat list selectors.
 | 
					
						
							|  |  |  | 		flat: false, | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:37:32 +03:00
										 |  |  | 	// XXX TEST: this should prevent event handler delegation...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	keyboard: { | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 		// filter mappings...
 | 
					
						
							|  |  |  | 		Filter: { | 
					
						
							|  |  |  | 			pattern: '.browse .path div.cur[contenteditable]', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 			// keep text editing action from affecting the selection...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			ignore: [ | 
					
						
							|  |  |  | 					'Backspace', | 
					
						
							|  |  |  | 					'Left', | 
					
						
							|  |  |  | 					'Right', | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 					'Home', | 
					
						
							|  |  |  | 					'End', | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 					'Enter', | 
					
						
							|  |  |  | 					'Esc', | 
					
						
							| 
									
										
										
										
											2015-06-18 01:41:57 +03:00
										 |  |  | 					'/', | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 				], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Enter: 'action!', | 
					
						
							|  |  |  | 			Esc: 'stopFilter!', | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		General: { | 
					
						
							|  |  |  | 			pattern: '.browse', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 02:34:06 +03:00
										 |  |  | 			Up: 'prev!', | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			Backspace: 'Up', | 
					
						
							| 
									
										
										
										
											2015-06-18 02:34:06 +03:00
										 |  |  | 			Down: 'next!', | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			Left: 'pop', | 
					
						
							|  |  |  | 			Right: 'push', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 			Home: 'select!: "first"', | 
					
						
							|  |  |  | 			End: 'select!: "last"', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// XXX add page up and page down...
 | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 			// XXX ctrl-Left to go to root/base/home
 | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			Enter: 'action', | 
					
						
							|  |  |  | 			Esc: 'close', | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			'/': 'startFilter!', | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | 	// proxy event api...
 | 
					
						
							|  |  |  | 	on: proxyToDom('on'), | 
					
						
							|  |  |  | 	one: proxyToDom('one'), | 
					
						
							|  |  |  | 	off: proxyToDom('off'), | 
					
						
							|  |  |  | 	trigger: proxyToDom('trigger'), | 
					
						
							|  |  |  | 	bind: proxyToDom('bind'), | 
					
						
							|  |  |  | 	unbind: proxyToDom('unbind'), | 
					
						
							|  |  |  | 	deligate: proxyToDom('deligate'), | 
					
						
							|  |  |  | 	undeligate: proxyToDom('undeligate'), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// specific events...
 | 
					
						
							|  |  |  | 	focus: proxyToDom('focus'), | 
					
						
							|  |  |  | 	blur: proxyToDom('blur'), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 	// XXX should these set both the options and dom???
 | 
					
						
							|  |  |  | 	get flat(){ | 
					
						
							|  |  |  | 		return !this.dom.hasClass('flat') || this.options.flat | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set flat(value){ | 
					
						
							|  |  |  | 		if(value){ | 
					
						
							|  |  |  | 			this.dom.addClass('flat') | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.dom.removeClass('flat') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.options.flat = value | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX should these set both the options and dom???
 | 
					
						
							|  |  |  | 	get traversable(){ | 
					
						
							|  |  |  | 		return !this.dom.hasClass('not-traversable') && this.options.traversable | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set traversable(value){ | 
					
						
							|  |  |  | 		if(value){ | 
					
						
							|  |  |  | 			this.dom.removeClass('not-traversable') | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.dom.addClass('not-traversable') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.options.traversable = value | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	// base api...
 | 
					
						
							|  |  |  | 	// NOTE: to avoid duplicating and syncing data, the actual path is 
 | 
					
						
							|  |  |  | 	//		stored in DOM...
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 	// NOTE: path does not include the currently selected list element,
 | 
					
						
							|  |  |  | 	// 		just the path to the current list...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	get path(){ | 
					
						
							|  |  |  | 		var skip = false | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 		return this.dom.find('.path .dir:not(.cur)') | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			.map(function(i, e){ return $(e).text() }) | 
					
						
							|  |  |  | 			.toArray() | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	set path(value){ | 
					
						
							|  |  |  | 		return this.update(value) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// update path...
 | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 	// 	- build the path
 | 
					
						
							|  |  |  | 	// 	- build the element list
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 	// 	- bind to control events
 | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 	// XXX do we normalize path here???
 | 
					
						
							|  |  |  | 	// XXX need a way to handle path errors in the extension API...
 | 
					
						
							|  |  |  | 	// 		...for example, if .list(..) can't list or lists a different
 | 
					
						
							|  |  |  | 	// 		path due to an error, we need to be able to render the new
 | 
					
						
							|  |  |  | 	// 		path both in the path and list sections...
 | 
					
						
							|  |  |  | 	// 		NOTE: current behaviour is not wrong, it just not too flexible...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	update: function(path){ | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 		path = path || this.path | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		var browser = this.dom | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 		// normalize path...
 | 
					
						
							|  |  |  | 		// XXX is it correct to ignore empty path elements, e.g. 'aa//cc'?
 | 
					
						
							|  |  |  | 		var splitter = /[\\\/]/ | 
					
						
							|  |  |  | 		if(typeof(path) == typeof('str') && splitter.test(path)){ | 
					
						
							|  |  |  | 			path = path | 
					
						
							|  |  |  | 				.split(splitter) | 
					
						
							|  |  |  | 				.filter(function(e){ return e != '' }) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		var p = browser.find('.path').empty() | 
					
						
							|  |  |  | 		var l = browser.find('.list').empty() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 		var c = [] | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		// fill the path field...
 | 
					
						
							|  |  |  | 		path.forEach(function(e){ | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 			c.push(e) | 
					
						
							|  |  |  | 			var cur = c.slice() | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			p.append($('<div>') | 
					
						
							|  |  |  | 				.addClass('dir') | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 				.click(function(){ | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 					if(that.traversable){ | 
					
						
							|  |  |  | 						that | 
					
						
							|  |  |  | 							.update(cur.slice(0, -1))  | 
					
						
							|  |  |  | 							.select('"'+cur.pop()+'"') | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 				.text(e)) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 		// add current selection indicator...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 		var txt | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 		p.append($('<div>') | 
					
						
							|  |  |  | 			.addClass('dir cur') | 
					
						
							|  |  |  | 			.click(function(){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 				that.startFilter() | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 				//that.update(path.concat($(this).text())) 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 01:41:57 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 				// XXX HACK: prevents the field from blurring when clicked...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 01:41:57 +03:00
										 |  |  | 				// 			...need to find a better way...
 | 
					
						
							|  |  |  | 				that._hold_blur = true | 
					
						
							|  |  |  | 				setTimeout(function(){ delete that._hold_blur }, 20) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			// XXX for some reason this gets triggered when clicking ano 
 | 
					
						
							|  |  |  | 			// 		is not triggered when entering via '/'
 | 
					
						
							|  |  |  | 			.on('blur', function(){ | 
					
						
							|  |  |  | 				// XXX HACK: prevents the field from bluring when clicked...
 | 
					
						
							|  |  |  | 				// 			...need to find a better way...
 | 
					
						
							|  |  |  | 				if(!that._hold_blur){ | 
					
						
							|  |  |  | 					that.stopFilter() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 			/* XXX does the right thing (replaces the later .focus(..)  | 
					
						
							|  |  |  | 			 * 		and .keyup(..)) but does not work in IE... | 
					
						
							|  |  |  | 			.on('input', function(){ | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 				that.showFiltered($(this).text()) | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 			*/ | 
					
						
							|  |  |  | 			// only update if text changed...
 | 
					
						
							|  |  |  | 			.focus(function(){ | 
					
						
							|  |  |  | 				txt = $(this).text() | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.keyup(function(){ | 
					
						
							|  |  |  | 				var cur  = $(this).text() | 
					
						
							|  |  |  | 				if(txt != cur){ | 
					
						
							|  |  |  | 					txt = cur | 
					
						
							|  |  |  | 					that.showFiltered(cur) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-03-21 18:20:59 +03:00
										 |  |  | 			})) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		// fill the children list...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 		var interactive = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var make = function(p){ | 
					
						
							|  |  |  | 			interactive = true | 
					
						
							|  |  |  | 			return $('<div>') | 
					
						
							|  |  |  | 				.click(function(){ | 
					
						
							|  |  |  | 					// handle clicks ONLY when not disabled...
 | 
					
						
							|  |  |  | 					if(!$(this).hasClass('disabled')){ | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 						that.push($(this).text())  | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				.text(p) | 
					
						
							|  |  |  | 				.appendTo(l) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var res = this.list(path, make) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(!interactive){ | 
					
						
							|  |  |  | 			res.forEach(make) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | 		this.trigger('update') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// Filter the item list...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	// 	General signature...
 | 
					
						
							|  |  |  | 	// 	.filter(<pattern>[, <rejected-handler>][, <ignore-disabled>])
 | 
					
						
							|  |  |  | 	// 		-> elements
 | 
					
						
							|  |  |  | 	// 	
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get all elements...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// 	.filter()
 | 
					
						
							|  |  |  | 	// 	.filter('*')
 | 
					
						
							|  |  |  | 	// 		-> all elements
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	// 	Get all elements containing a string...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// 	.filter(<string>)
 | 
					
						
							|  |  |  | 	// 		-> elements
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	// 	Get all elements matching a regexp...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:18:28 +03:00
										 |  |  | 	// 	.filter(<regexp>)
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// 		-> elements
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	// 	Filter the elements via a function...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// 	.filter(<function>)
 | 
					
						
							|  |  |  | 	// 		-> elements
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	// 		NOTE: the elements passed to the <function> on each iteration
 | 
					
						
							|  |  |  | 	// 			are unwrapped for compatibility with jQuery API.
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	// 	Get specific element...
 | 
					
						
							|  |  |  | 	// 	.filter(<index>)
 | 
					
						
							|  |  |  | 	// 	.filter(<jQuery-obj>)
 | 
					
						
							|  |  |  | 	// 		-> element
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:51:11 +03:00
										 |  |  | 	//		-> $()
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	// 		NOTE: when passing a jQuery-obj it will be returned iff it's
 | 
					
						
							|  |  |  | 	// 			an element.
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:51:11 +03:00
										 |  |  | 	// 		NOTE: unlike .select(..) index overflow will produce empty 
 | 
					
						
							|  |  |  | 	// 			lists rather than to/bottom elements.
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	// If <rejected-handler> function is passed it will get called with 
 | 
					
						
							|  |  |  | 	// every element that was rejected by the predicate / not matching 
 | 
					
						
							|  |  |  | 	// the pattern.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// By default, <ignore-disabled> is false, thus this will ignore 
 | 
					
						
							|  |  |  | 	// disabled elements. If <ignore_disabled> is false then disabled 
 | 
					
						
							|  |  |  | 	// elements will be searched too.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:18:28 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 	// Extended string patterns:
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// The pattern string is split by whitespace and each resulting 
 | 
					
						
							|  |  |  | 	// substring is searched independently.
 | 
					
						
							|  |  |  | 	// Order is not considered.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Examples:
 | 
					
						
							|  |  |  | 	// 		'aaa'			- matches any element containing 'aaa'
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	// 							(Same as: /aaa/)
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 	// 		'aa bb'			- matches any element containing both 'aa'
 | 
					
						
							|  |  |  | 	// 							AND 'bb' in any order.
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	// 							(Same as: /aa.*bb|bb.*aa/)
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 	// NOTE: currently there is no way to search for whitespace explicitly,
 | 
					
						
							|  |  |  | 	// 		at this point this is "by-design" as an experiment on how
 | 
					
						
							|  |  |  | 	// 		vital this feature is.
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 	// TODO need to support glob / nested patterns...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// 		..things like /**/a*/*moo/
 | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX add * support...
 | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 	filter: function(pattern, a, b){ | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 		pattern = pattern == null ? '*' : pattern | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 		var ignore_disabled = typeof(a) == typeof(true) ? a : b | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 		ignore_disabled = ignore_disabled == null ? true : ignore_disabled | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 		var rejected = typeof(a) == typeof(true) ? null : a | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var browser = this.dom | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 		var elems = browser.find('.list>div' + (ignore_disabled ? ':not(.disabled)' : '')) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if(pattern == '*'){ | 
					
						
							|  |  |  | 			return elems  | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// function...
 | 
					
						
							|  |  |  | 		if(typeof(pattern) == typeof(function(){})){ | 
					
						
							|  |  |  | 			var filter = function(i, e){ | 
					
						
							| 
									
										
										
										
											2015-06-21 20:19:15 +03:00
										 |  |  | 				e = e[0] | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 				if(!pattern.call(e, i, e)){ | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 					if(rejected){ | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 						rejected.call(e, i, e) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					return false | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// regexp...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 		} else if(pattern.constructor == RegExp){ | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			var filter = function(i, e){ | 
					
						
							|  |  |  | 				if(!pattern.test($(e).text())){ | 
					
						
							|  |  |  | 					if(rejected){ | 
					
						
							| 
									
										
										
										
											2015-06-19 19:13:11 +03:00
										 |  |  | 						rejected.call(e, i, e) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					return false | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// string...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 		// NOTE: this supports several space-separated patterns.
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 		// XXX support glob...
 | 
					
						
							|  |  |  | 		} else if(typeof(pattern) == typeof('str')){ | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 			var pl = pattern.trim().split(/\s+/) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			var filter = function(i, e){ | 
					
						
							|  |  |  | 				e = $(e) | 
					
						
							|  |  |  | 				var t = e.text() | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 				for(var p=0; p < pl.length; p++){ | 
					
						
							|  |  |  | 					var i = t.search(pl[p]) | 
					
						
							|  |  |  | 					if(!(i >= 0)){ | 
					
						
							|  |  |  | 						if(rejected){ | 
					
						
							|  |  |  | 							rejected.call(e, i, e) | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return false | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// number...
 | 
					
						
							|  |  |  | 		} else if(typeof(pattern) == typeof(123)){ | 
					
						
							|  |  |  | 			return elems.eq(pattern) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// jQuery object...
 | 
					
						
							|  |  |  | 		} else if(elems.index(pattern) >= 0){ | 
					
						
							|  |  |  | 			return pattern | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// unknown pattern...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return $() | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return elems.filter(filter) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:18:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// internal actions...
 | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// NOTE: this uses .filter(..) for actual filtering...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 	// XXX revise API -- seems a bit overcomplicated...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	showFiltered: function(pattern){ | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 		var browser = this.dom | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 17:42:48 +03:00
										 |  |  | 		// show all...
 | 
					
						
							|  |  |  | 		if(pattern == null || pattern.trim() == '*'){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			browser.find('.filtered-out') | 
					
						
							|  |  |  | 				.removeClass('filtered-out') | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			// clear the highlighting...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			browser.find('.list b') | 
					
						
							|  |  |  | 				.replaceWith(function() { return this.innerHTML }) | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-30 17:42:48 +03:00
										 |  |  | 		// basic filter...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 			var p = RegExp('(' + pattern.trim().split(/\s+/).join('|') + ')', 'g') | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			this.filter(pattern, | 
					
						
							|  |  |  | 					// rejected...
 | 
					
						
							|  |  |  | 					function(i, e){ | 
					
						
							|  |  |  | 						e | 
					
						
							|  |  |  | 							.addClass('filtered-out') | 
					
						
							|  |  |  | 							.removeClass('selected') | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 					}, | 
					
						
							|  |  |  | 					// NOTE: setting this to true will not remove disabled
 | 
					
						
							|  |  |  | 					// 		elements from view as they will neither get 
 | 
					
						
							|  |  |  | 					// 		included in the filter not in the filtered out
 | 
					
						
							|  |  |  | 					// 		thus it will require manual setting of the
 | 
					
						
							|  |  |  | 					// 		.filtered-out class
 | 
					
						
							|  |  |  | 					false) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 				// passed...
 | 
					
						
							|  |  |  | 				.removeClass('filtered-out') | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 				// NOTE: this will mess up (clear) any highlighting that was 
 | 
					
						
							|  |  |  | 				// 		present before...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 				.each(function(_, e){ | 
					
						
							|  |  |  | 					e = $(e) | 
					
						
							|  |  |  | 					var t = e.text() | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 					e.html(t.replace(p, '<b>$1</b>')) | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2015-03-30 17:42:48 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							| 
									
										
										
										
											2015-03-22 02:12:30 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2015-06-21 20:37:32 +03:00
										 |  |  | 	// XXX should this be a toggler???
 | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 	startFilter: function(){ | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 		if(this.options.filter){ | 
					
						
							|  |  |  | 			var range = document.createRange() | 
					
						
							|  |  |  | 			var selection = window.getSelection() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 			var e = this.dom.find('.path .dir.cur') | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 				//.text('')
 | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 				.attr('contenteditable', true) | 
					
						
							|  |  |  | 				.focus() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// place the cursor...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 05:57:15 +03:00
										 |  |  | 			//range.setStart(e[0], 0)
 | 
					
						
							|  |  |  | 			//range.collapse(true)
 | 
					
						
							|  |  |  | 			range.selectNodeContents(e[0]) | 
					
						
							| 
									
										
										
										
											2015-06-20 20:51:43 +03:00
										 |  |  | 			selection.removeAllRanges() | 
					
						
							|  |  |  | 			selection.addRange(range) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	stopFilter: function(){ | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 		this.showFiltered('*') | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 		this.dom.find('.path .dir.cur') | 
					
						
							|  |  |  | 			.text('') | 
					
						
							|  |  |  | 			.removeAttr('contenteditable') | 
					
						
							|  |  |  | 		this | 
					
						
							|  |  |  | 			.focus() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 18:17:37 +03:00
										 |  |  | 		// NOTE: we might select an item outside of the current visible
 | 
					
						
							|  |  |  | 		// 		area, thus re-selecting it after we remove the filter 
 | 
					
						
							|  |  |  | 		// 		will place it correctly.
 | 
					
						
							|  |  |  | 		this.select(this.select('!')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	get filtering(){ | 
					
						
							|  |  |  | 		return this.dom.find('.path .dir.cur[contenteditable]').length > 0  | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2015-06-18 00:23:23 +03:00
										 |  |  | 	toggleFilterMode: function(){ | 
					
						
							|  |  |  | 		this.dom.toggleClass('show-filtered-out') | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	// XXX should this be a toggler???
 | 
					
						
							|  |  |  | 	disableElements: function(pattern){ | 
					
						
							|  |  |  | 		this.filter(pattern, false) | 
					
						
							|  |  |  | 			.addClass('disabled') | 
					
						
							|  |  |  | 			.removeClass('selected') | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	enableElements: function(pattern){ | 
					
						
							|  |  |  | 		this.filter(pattern, false) | 
					
						
							|  |  |  | 			.removeClass('disabled') | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	// Select a list element...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-05-17 03:52:21 +03:00
										 |  |  | 	//	Get selected element if it exists, otherwise select and return 
 | 
					
						
							|  |  |  | 	//	the first...
 | 
					
						
							|  |  |  | 	//	.select()
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Get selected element if it exists, null otherwise...
 | 
					
						
							|  |  |  | 	//	.select('!')
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//		-> $()
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//	Select first/last child
 | 
					
						
							|  |  |  | 	//	.select('first')
 | 
					
						
							|  |  |  | 	//	.select('last')
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-05-17 03:52:21 +03:00
										 |  |  | 	//	Select previous/next child
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//	.select('prev')
 | 
					
						
							|  |  |  | 	//	.select('next')
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Deselect
 | 
					
						
							|  |  |  | 	//	.select('none')
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	//		-> $()
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Select element by sequence number
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	//	NOTE: negative numbers count from the tail.
 | 
					
						
							|  |  |  | 	//	NOTE: overflowing selects the first/last element.
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//	.select(<number>)
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Select element by its text...
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	//	NOTE: if text matches one of the reserved commands above use 
 | 
					
						
							|  |  |  | 	//		quotes to escape it...
 | 
					
						
							|  |  |  | 	//	.select('<text>')
 | 
					
						
							|  |  |  | 	//	.select("'<text>'")
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//	.select('"<text>"')
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	//	Select element via a regular expression...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	//	.select(<regexp>)
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	//		-> elem
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	//		-> $()
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	//	Select jQuery object...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//	.select(<elem>)
 | 
					
						
							|  |  |  | 	//		-> elem
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	//		-> $()
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// This will return a jQuery object.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 	// NOTE: if multiple matches occur this will select the first.
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 	// NOTE: 'none' will always return an empty jQuery object, to get 
 | 
					
						
							|  |  |  | 	// 		the selection state before deselecting use .select('!')
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 	// NOTE: this uses .filter(..) for string and regexp matching...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 	select: function(elem, filtering){ | 
					
						
							|  |  |  | 		var pattern = '.list div:not(.disabled):not(.filtered-out)' | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		var browser = this.dom | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 		var elems = browser.find(pattern) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		filtering = filtering == null ? this.filtering : filtering | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if(elems.length == 0){ | 
					
						
							|  |  |  | 			return $() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 		// empty list/string selects none...
 | 
					
						
							|  |  |  | 		elem = elem != null && elem.length == 0 ? 'none' : elem | 
					
						
							|  |  |  | 		// 0 or no args (null) selects first...
 | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 		elem = elem == 0 ? 'first' : elem | 
					
						
							|  |  |  | 		// no args -> either we start with the selected or the first...
 | 
					
						
							|  |  |  | 		if(elem == null){ | 
					
						
							|  |  |  | 			var cur = this.select('!') | 
					
						
							|  |  |  | 			elem = cur.length == 0 ? 'first' : cur | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// first/last...
 | 
					
						
							|  |  |  | 		if(elem == 'first' || elem == 'last'){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			return this.select(elems[elem](), filtering) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		 | 
					
						
							|  |  |  | 		// prev/next...
 | 
					
						
							|  |  |  | 		} else if(elem == 'prev' || elem == 'next'){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			var to = this.select('!', filtering)[elem + 'All'](pattern).first() | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			if(to.length == 0){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 				return this.select(elem == 'prev' ? 'last' : 'first', filtering) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			this.select('none', filtering) | 
					
						
							|  |  |  | 			return this.select(to, filtering) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// deselect...
 | 
					
						
							|  |  |  | 		} else if(elem == 'none'){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			if(!filtering){ | 
					
						
							|  |  |  | 				browser.find('.path .dir.cur').empty() | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 			elems = elems | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 				.filter('.selected') | 
					
						
							|  |  |  | 				.removeClass('selected') | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 			this.trigger('deselect', elems) | 
					
						
							| 
									
										
										
										
											2015-06-18 17:13:04 +03:00
										 |  |  | 			return $() | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// strict...
 | 
					
						
							|  |  |  | 		} else if(elem == '!'){ | 
					
						
							|  |  |  | 			return elems.filter('.selected') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// number...
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		// NOTE: on overflow this will get the first/last element...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		} else if(typeof(elem) == typeof(123)){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 			return this.select($(elems.slice(elem)[0] || elems.slice(-1)[0] ), filtering) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// string...
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		} else if(typeof(elem) == typeof('str')){ | 
					
						
							|  |  |  | 			if(/^'.*'$|^".*"$/.test(elem.trim())){ | 
					
						
							|  |  |  | 				elem = elem.trim().slice(1, -1) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			return this.select(this.filter(elem).first(), filtering) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		// regexp...
 | 
					
						
							|  |  |  | 		} else if(elem.constructor === RegExp){ | 
					
						
							| 
									
										
										
										
											2015-06-18 18:09:02 +03:00
										 |  |  | 			return this.select(this.filter(elem).first(), filtering) | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		// element...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 			elem = $(elem).first() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if(elem.length == 0){ | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 				this.select(null, filtering) | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2015-06-18 00:13:30 +03:00
										 |  |  | 				this.select('none', filtering) | 
					
						
							|  |  |  | 				if(!filtering){ | 
					
						
							|  |  |  | 					browser.find('.path .dir.cur').text(elem.text()) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-06-18 02:34:06 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// handle scroll position...
 | 
					
						
							|  |  |  | 				var p = elem.scrollParent() | 
					
						
							|  |  |  | 				var S = p.scrollTop() | 
					
						
							|  |  |  | 				var H = p.height() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var h = elem.height() | 
					
						
							|  |  |  | 				var t = elem.offset().top - p.offset().top | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var D = 3 * h  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// too low...
 | 
					
						
							|  |  |  | 				if(t+h+D > H){ | 
					
						
							|  |  |  | 					p.scrollTop(S + (t+h+D) - H) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// too high...
 | 
					
						
							|  |  |  | 				} else if(t < D){ | 
					
						
							|  |  |  | 					p.scrollTop(S + t - D) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 				elem.addClass('selected') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | 				this.trigger('select', elem) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 				return elem | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-17 03:52:21 +03:00
										 |  |  | 	// Select next element...
 | 
					
						
							|  |  |  | 	next: function(elem){ | 
					
						
							|  |  |  | 		if(elem != null){ | 
					
						
							|  |  |  | 			this.select(elem) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.select('next') | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	// Select previous element...
 | 
					
						
							|  |  |  | 	prev: function(elem){ | 
					
						
							|  |  |  | 		if(elem != null){ | 
					
						
							|  |  |  | 			this.select(elem) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.select('prev') | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Push an element to path / go down one level...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 	// XXX trigger a "push" event... (???)
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	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...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		if(!this.traversable || elem.hasClass('not-traversable')){ | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 			return this.action(path) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.path = path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.select() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2015-05-17 03:52:21 +03:00
										 |  |  | 	// Pop an element off the path / go up one level...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 	// XXX trigger a "pop" event... (???)
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	pop: function(){ | 
					
						
							|  |  |  | 		var browser = this.dom | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if(!this.traversable){ | 
					
						
							|  |  |  | 			return this | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		var path = this.path | 
					
						
							|  |  |  | 		var dir = path.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.update(path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.select('"'+dir+'"') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX think about the API...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:02:58 +03:00
										 |  |  | 	// XXX need to check if openable i.e. when to use open and when push...
 | 
					
						
							|  |  |  | 	// XXX might be a good idea to add a live traversable check...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	action: function(){ | 
					
						
							|  |  |  | 		var elem = this.select('!') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// nothing selected, select first and exit...
 | 
					
						
							|  |  |  | 		if(elem.length == 0){ | 
					
						
							|  |  |  | 			this.select() | 
					
						
							|  |  |  | 			return this | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		var path = this.path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		path.push(elem.text()) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var res = this.open(path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	// extension methods...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Open action...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | 	// XXX do we need to pass this to the event???
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	open: function(path){  | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		path = path || this.path | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		var m = this.options.open | 
					
						
							| 
									
										
										
										
											2015-06-22 06:01:54 +03:00
										 |  |  | 		var res = m ? m.apply(this, arguments) : path | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | 		var elem = this.select('!') | 
					
						
							| 
									
										
										
										
											2015-06-22 06:01:54 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | 		// XXX do we need to pass this to the event???
 | 
					
						
							| 
									
										
										
										
											2015-06-22 06:01:54 +03:00
										 |  |  | 		this.trigger('open', path) | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | 		if(elem.length > 0){ | 
					
						
							|  |  |  | 			// XXX do we need to pass this to the event???
 | 
					
						
							|  |  |  | 			elem.trigger('open', path) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-06-22 06:01:54 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return res | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2015-06-21 19:58:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	// List the path...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This will get passed a path and an item constructor and should 
 | 
					
						
							|  |  |  | 	// return a list.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// There are two mods of operation:
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 1) interactive:
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:54:21 +03:00
										 |  |  | 	// 		.list(path, make)
 | 
					
						
							|  |  |  | 	// 			- for each item make is called with it's text
 | 
					
						
							|  |  |  | 	//			- make will return a jQuery object of the item
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		NOTE: selection is currently done based on .text() thus the 
 | 
					
						
							|  |  |  | 	// 			modification should not affect it's output...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 2) non-interactive:
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:54:21 +03:00
										 |  |  | 	// 		.list(path) -> list
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:00:55 +03:00
										 |  |  | 	// 			- .list(..) should return an array
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:54:21 +03:00
										 |  |  | 	// 			- make should never get called
 | 
					
						
							|  |  |  | 	// 			- the returned list will be rendered
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2015-06-21 04:15:54 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// This can set the following classes on elements:
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.disabled
 | 
					
						
							|  |  |  | 	// 		an element is disabled.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.non-traversable
 | 
					
						
							|  |  |  | 	// 		an element is not traversable/listable and will trigger the
 | 
					
						
							|  |  |  | 	// 		.action(..) on push...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 	list: function(path, make){ | 
					
						
							| 
									
										
										
										
											2015-05-25 20:02:47 +03:00
										 |  |  | 		path = path || this.path | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 		var m = this.options.list | 
					
						
							| 
									
										
										
										
											2015-06-21 03:49:00 +03:00
										 |  |  | 		return m ? m.apply(this, arguments) : [] | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 20:37:32 +03:00
										 |  |  | 	// XXX need to get a container -- UI widget API....
 | 
					
						
							| 
									
										
										
										
											2015-06-22 04:08:24 +03:00
										 |  |  | 	// XXX trigger started event...
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	__init__: function(parent, options){ | 
					
						
							| 
									
										
										
										
											2015-05-18 03:34:15 +03:00
										 |  |  | 		options = options || {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// merge options...
 | 
					
						
							|  |  |  | 		var opts = Object.create(this.options) | 
					
						
							|  |  |  | 		Object.keys(options).forEach(function(n){ opts[n] = options[n] }) | 
					
						
							|  |  |  | 		options = this.options = opts | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// build the dom...
 | 
					
						
							|  |  |  | 		var dom = this.dom = this.constructor.make(options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add keyboard handler...
 | 
					
						
							|  |  |  | 		dom.keydown( | 
					
						
							|  |  |  | 			keyboard.makeKeyboardHandler( | 
					
						
							|  |  |  | 				this.keyboard, | 
					
						
							| 
									
										
										
										
											2015-05-18 03:34:15 +03:00
										 |  |  | 				options.logKeys, | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 				this)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// attach to parent...
 | 
					
						
							|  |  |  | 		if(parent != null){ | 
					
						
							|  |  |  | 			parent.append(dom) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// load the initial state...
 | 
					
						
							| 
									
										
										
										
											2015-06-21 16:12:20 +03:00
										 |  |  | 		// XXX check if this default is correct...
 | 
					
						
							|  |  |  | 		this.update(options.path || this.path) | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var Browser =  | 
					
						
							| 
									
										
										
										
											2015-06-20 14:50:54 +03:00
										 |  |  | module.Browser =  | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | object.makeConstructor('Browser',  | 
					
						
							|  |  |  | 		BrowserClassPrototype,  | 
					
						
							|  |  |  | 		BrowserPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-22 18:09:37 +03:00
										 |  |  | // Flat list...
 | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2015-06-22 18:09:37 +03:00
										 |  |  | // This expects a data option set with the following format:
 | 
					
						
							|  |  |  | // 	{
 | 
					
						
							|  |  |  | // 		<option-text>: <callback>,
 | 
					
						
							|  |  |  | // 		...
 | 
					
						
							|  |  |  | // 	}
 | 
					
						
							|  |  |  | // 	
 | 
					
						
							|  |  |  | // NOTE: this essentially a different default configuration of Browser...
 | 
					
						
							|  |  |  | var ListPrototype = Object.create(BrowserPrototype) | 
					
						
							|  |  |  | ListPrototype.options = { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	traversable: false, | 
					
						
							|  |  |  | 	flat: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list: function(path, make){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return Object.keys(this.options.data) | 
					
						
							|  |  |  | 			.map(function(k){ | 
					
						
							|  |  |  | 				var e = make(k) | 
					
						
							|  |  |  | 					.on('open', function(){  | 
					
						
							|  |  |  | 						return that.options.data[k].apply(this, arguments) | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return k | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ListPrototype.options.__proto__ = BrowserPrototype.options | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var List =  | 
					
						
							|  |  |  | module.List =  | 
					
						
							|  |  |  | object.makeConstructor('List',  | 
					
						
							|  |  |  | 		BrowserClassPrototype,  | 
					
						
							|  |  |  | 		ListPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is a shorthand for: new List(<elem>, { data: <list> })
 | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | var makeList =  | 
					
						
							|  |  |  | module.makeList = function(elem, list){ | 
					
						
							| 
									
										
										
										
											2015-06-22 18:09:37 +03:00
										 |  |  | 	return List(elem, { data: list }) | 
					
						
							| 
									
										
										
										
											2015-06-22 17:22:01 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-21 16:45:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                                                */ | 
					
						
							| 
									
										
										
										
											2015-06-20 14:50:54 +03:00
										 |  |  | return module }) |