| 
									
										
										
										
											2024-10-27 11:01:12 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //var DEBUG = DEBUG != null ? DEBUG : true
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * Modal dialogs... | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************* Helpers ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Set element text and tooltip
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: when text is a list, we will only use the first and the last 
 | 
					
						
							|  |  |  | // 		elements...
 | 
					
						
							|  |  |  | // NOTE: if tip_elem is not given then both the text and tip will be set
 | 
					
						
							|  |  |  | // 		on text_elem
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX add support for quoted '|'...
 | 
					
						
							|  |  |  | function setTextWithTooltip(text, text_elem, tip_elem){ | 
					
						
							|  |  |  | 	text_elem = $(text_elem) | 
					
						
							|  |  |  | 	tip_elem = tip_elem == null ? text_elem : tip_elem | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(typeof(text) != typeof('str')){ | 
					
						
							|  |  |  | 		tip = text | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		var tip = text.split(/\s*\|\s*/) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// set elemnt text...
 | 
					
						
							|  |  |  | 	text_elem | 
					
						
							|  |  |  | 		.html(tip[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// do the tooltip...
 | 
					
						
							|  |  |  | 	tip = tip.slice(1) | 
					
						
							|  |  |  | 	tip = tip[tip.length-1] | 
					
						
							|  |  |  | 	if(tip != null && tip.trim().length > 0){ | 
					
						
							|  |  |  | 		$('<span class="tooltip-icon tooltip-right"> *</span>') | 
					
						
							|  |  |  | 			.attr('tooltip', tip) | 
					
						
							|  |  |  | 			.appendTo(tip_elem) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return text_elem | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getOverlay(root){ | 
					
						
							|  |  |  | 	root = $(root) | 
					
						
							|  |  |  | 	var overlay = root.find('.overlay-block') | 
					
						
							|  |  |  | 	if(overlay.length == 0){ | 
					
						
							|  |  |  | 		return $('<div class="overlay-block">'+ | 
					
						
							|  |  |  | 					'<div class="background"/>'+ | 
					
						
							|  |  |  | 					'<div class="content"/>'+ | 
					
						
							|  |  |  | 				'</div>').appendTo(root) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return overlay | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function showInOverlay(root, data){ | 
					
						
							|  |  |  | 	root = $(root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var overlay = getOverlay(root) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(data != null){ | 
					
						
							|  |  |  | 		var container = $('<table width="100%" height="100%"><tr><td align="center" valign="center">'+ | 
					
						
							|  |  |  | 								'<div class="dialog"/>'+ | 
					
						
							|  |  |  | 							'</td></tr></table>') | 
					
						
							|  |  |  | 		var dialog = container.find('.dialog') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//overlay.find('.background')
 | 
					
						
							|  |  |  | 		//	.click(function(){ hideOverlay(root) })
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dialog | 
					
						
							|  |  |  | 			.append(data) | 
					
						
							|  |  |  | 			.on('click', function(evt){  | 
					
						
							|  |  |  | 				evt.stopPropagation()  | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		overlay.find('.content') | 
					
						
							|  |  |  | 			.on('click', function(){  | 
					
						
							|  |  |  | 				overlay.trigger('close') | 
					
						
							|  |  |  | 				hideOverlay(root)  | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.on('close accept', function(){ | 
					
						
							|  |  |  | 				//hideOverlay(root) 
 | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.append(container) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	root.addClass('overlay') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return overlay | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function hideOverlay(root){ | 
					
						
							|  |  |  | 	root.removeClass('overlay') | 
					
						
							|  |  |  | 	root.find('.overlay-block') | 
					
						
							|  |  |  | 		.trigger('close') | 
					
						
							|  |  |  | 		.remove() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function isOverlayVisible(root){ | 
					
						
							|  |  |  | 	return getOverlay(root).css('display') != 'none' | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * Field definitions... | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var FIELD_TYPES = { | 
					
						
							|  |  |  | 	// a simple hr...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// format:
 | 
					
						
							|  |  |  | 	// 		'---'
 | 
					
						
							|  |  |  | 	// 		Three or more '-'s
 | 
					
						
							|  |  |  | 	hr: { | 
					
						
							|  |  |  | 		type: 'hr', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<hr>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return /\-\-\-+/.test(val) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	// a simple br...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// format:
 | 
					
						
							|  |  |  | 	// 		'   '
 | 
					
						
							|  |  |  | 	// 		Three or more spaces
 | 
					
						
							|  |  |  | 	br: { | 
					
						
							|  |  |  | 		type: 'br', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<br>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return /\s\s\s+/.test(val) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	// format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		html: <html-block>
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	html: { | 
					
						
							|  |  |  | 		type: 'html', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="html-block"/>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return val.html != null | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			if(typeof(value.html) == typeof('str')){ | 
					
						
							|  |  |  | 				field.html(value.html) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				field.append(value.html) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 		string
 | 
					
						
							|  |  |  | 	// XXX add datalist option...
 | 
					
						
							|  |  |  | 	// XXX make this textarea compatible...
 | 
					
						
							|  |  |  | 	text: { | 
					
						
							|  |  |  | 		type: 'text', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: '', | 
					
						
							|  |  |  | 		html: '<div class="field string">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<input type="text" class="value">'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return typeof(val) == typeof('abc') | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			$(field).find('.value').attr('value', value)  | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return $(field).find('.value').attr('value')  | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	},	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 		true | false
 | 
					
						
							|  |  |  | 	bool: { | 
					
						
							|  |  |  | 		type: 'bool', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field checkbox">'+ | 
					
						
							|  |  |  | 				'<label><input type="checkbox" class="value">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span></label>'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return val === true || val === false | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			if(value){ | 
					
						
							|  |  |  | 				$(field).find('.value').attr('checked', '')  | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				$(field).find('.value').removeAttr('checked')  | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return $(field).find('.value').attr('checked') == 'checked' | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: this will not work without node-webkit...
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 		{ dir: <default-path> }
 | 
					
						
							|  |  |  | 	dir: { | 
					
						
							|  |  |  | 		type: 'dir', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field checkbox">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<input type="file" class="value" nwdirectory />'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return typeof(val) == typeof({}) && 'dir' in val | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			field.find('.value').attr('nwworkingdir', value.dir) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			var f = $(field).find('.value')[0].files | 
					
						
							|  |  |  | 			if(f.length == 0){ | 
					
						
							|  |  |  | 				return '' | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return f[0].path | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: this will not work without node-webkit...
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 		{ dir: <default-path> }
 | 
					
						
							|  |  |  | 	// XXX add datalist option...
 | 
					
						
							|  |  |  | 	ndir: { | 
					
						
							|  |  |  | 		type: 'ndir', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field dir">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<input type="text" class="path"/>'+ | 
					
						
							|  |  |  | 				'<button class="browse">Browse</button>'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return typeof(val) == typeof({}) && 'ndir' in val | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// NOTE: we are attaching the file browser to body to avoid 
 | 
					
						
							|  |  |  | 			// 		click events on it closing the dialog...
 | 
					
						
							|  |  |  | 			// 		...for some reason stopPropagation(...) does not do 
 | 
					
						
							|  |  |  | 			// 		the job...
 | 
					
						
							|  |  |  | 			var file = $('<input type="file" class="value" nwdirectory/>') | 
					
						
							|  |  |  | 				.attr('nwworkingdir', value.ndir) | 
					
						
							|  |  |  | 				.change(function(){ | 
					
						
							|  |  |  | 					var p = file[0].files | 
					
						
							|  |  |  | 					if(p.length != 0){ | 
					
						
							|  |  |  | 						field.find('.path').val(p[0].path) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					file.detach() | 
					
						
							|  |  |  | 					// focus+select the path field...
 | 
					
						
							|  |  |  | 					// NOTE: this is here to enable fast select-open 
 | 
					
						
							|  |  |  | 					// 		keyboard cycle (tab, enter, <select path>, 
 | 
					
						
							|  |  |  | 					// 		enter, enter)...
 | 
					
						
							|  |  |  | 					field.find('.path') | 
					
						
							|  |  |  | 						.focus() | 
					
						
							|  |  |  | 						.select() | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				.hide() | 
					
						
							|  |  |  | 			field.find('.path').val(value.ndir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			field.find('.browse').click(function(){ | 
					
						
							|  |  |  | 				file | 
					
						
							|  |  |  | 					// load user input path...
 | 
					
						
							|  |  |  | 					.attr('nwworkingdir', field.find('.path').val()) | 
					
						
							|  |  |  | 					.appendTo($('body')) | 
					
						
							|  |  |  | 					.click() | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return field.find('.path').val() | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 		['a', 'b', 'c', ...]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// an item can be of the folowing format:
 | 
					
						
							|  |  |  | 	// 		<text> ['|' 'default' | 'disabled' ] [ '|' <tool-tip> ]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: only one 'default' item should be present.
 | 
					
						
							|  |  |  | 	// NOTE: if no defaults are set, then the first item is checked.
 | 
					
						
							|  |  |  | 	choice: { | 
					
						
							|  |  |  | 		type: 'choice', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field choice">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<div class="item"><label>'+ | 
					
						
							|  |  |  | 					'<input type="radio" class="value"/>'+ | 
					
						
							|  |  |  | 					'<span class="item-text"></span>'+ | 
					
						
							|  |  |  | 				'</label></div>'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return typeof(val) == typeof([]) && val.constructor.name == 'Array' | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			var t = field.find('.text').html() | 
					
						
							|  |  |  | 			t = t == '' ? Math.random()+'' : t | 
					
						
							|  |  |  | 			var item = field.find('.item').last() | 
					
						
							|  |  |  | 			for(var i=0; i < value.length; i++){ | 
					
						
							|  |  |  | 				// get options...
 | 
					
						
							|  |  |  | 				var opts = value[i] | 
					
						
							|  |  |  | 					.split(/\|/g) | 
					
						
							|  |  |  | 					.map(function(e){ return e.trim() }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var val = item.find('.value') | 
					
						
							|  |  |  | 				val.val(opts[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// set checked state...
 | 
					
						
							|  |  |  | 				if(opts.slice(1).indexOf('default') >= 0){ | 
					
						
							|  |  |  | 					val.prop('checked', true) | 
					
						
							|  |  |  | 					opts.splice(opts.indexOf('default'), 1) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					val.prop('checked', false) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// set disabled state...
 | 
					
						
							|  |  |  | 				if(opts.slice(1).indexOf('disabled') >= 0){ | 
					
						
							|  |  |  | 					val.prop('disabled', true) | 
					
						
							|  |  |  | 					opts.splice(opts.indexOf('disabled'), 1) | 
					
						
							|  |  |  | 					item.addClass('disabled') | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					val.prop('disabled', false) | 
					
						
							|  |  |  | 					item.removeClass('disabled') | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				setTextWithTooltip(opts, item.find('.item-text')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				item.appendTo(field) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				item = item.clone() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var values = field.find('.value') | 
					
						
							|  |  |  | 				.attr('name', t) | 
					
						
							|  |  |  | 			// set the default...
 | 
					
						
							|  |  |  | 			if(values.filter(':checked:not([disabled])').length == 0){ | 
					
						
							|  |  |  | 				values.filter(':not([disabled])').first() | 
					
						
							|  |  |  | 					.prop('checked', true) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return $(field).find('.value:checked').val() | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 	{ 
 | 
					
						
							|  |  |  | 	// 		select: ['a', 'b', 'c', ...] 
 | 
					
						
							|  |  |  | 	// 		// default option (optional)...
 | 
					
						
							|  |  |  | 	// 		default: <number> | <text>
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	select: { | 
					
						
							|  |  |  | 		type: 'select', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field choice">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<select>'+ | 
					
						
							|  |  |  | 					'<option class="option"></option>'+ | 
					
						
							|  |  |  | 				'</select>'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return 'select' in val | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			var t = field.find('.text').text() | 
					
						
							|  |  |  | 			var item = field.find('.option').last() | 
					
						
							|  |  |  | 			var select = field.find('select') | 
					
						
							|  |  |  | 			for(var i=0; i < value.select.length; i++){ | 
					
						
							|  |  |  | 				item | 
					
						
							|  |  |  | 					.html(value.select[i]) | 
					
						
							|  |  |  | 					.val(value.select[i]) | 
					
						
							|  |  |  | 				item.appendTo(select) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				item = item.clone() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(value.default != null){ | 
					
						
							|  |  |  | 				if(typeof(value.default) == typeof(123)){ | 
					
						
							|  |  |  | 					field.find('.option') | 
					
						
							|  |  |  | 						.eq(value.default) | 
					
						
							|  |  |  | 							.attr('selected', '') | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					field.find('.option[value="'+ value.default +'"]') | 
					
						
							|  |  |  | 						.attr('selected', '') | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return $(field).find('.option:selected').val() | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: a button can have state...
 | 
					
						
							|  |  |  | 	// format: 
 | 
					
						
							|  |  |  | 	// 	{ 
 | 
					
						
							|  |  |  | 	// 		// click event handler...
 | 
					
						
							|  |  |  | 	// 		button: <function>, 
 | 
					
						
							|  |  |  | 	// 		// optional, button text (default 'OK')...
 | 
					
						
							|  |  |  | 	// 		text: <button-label>,
 | 
					
						
							|  |  |  | 	// 		// optional, initial state setup...
 | 
					
						
							|  |  |  | 	// 		default: <function>,
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	button: { | 
					
						
							|  |  |  | 		type: 'button', | 
					
						
							|  |  |  | 		text: null, | 
					
						
							|  |  |  | 		default: false, | 
					
						
							|  |  |  | 		html: '<div class="field button">'+ | 
					
						
							|  |  |  | 				'<span class="text"></span>'+ | 
					
						
							|  |  |  | 				'<button class="button"></button>'+ | 
					
						
							|  |  |  | 			'</div>', | 
					
						
							|  |  |  | 		test: function(val){ | 
					
						
							|  |  |  | 			return 'button' in val | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		set: function(field, value){ | 
					
						
							|  |  |  | 			var btn = $(field).find('button') | 
					
						
							|  |  |  | 				.click(value.button) | 
					
						
							|  |  |  | 				.html(value.text == null ? 'OK' : value.text) | 
					
						
							|  |  |  | 			if('default' in value){ | 
					
						
							|  |  |  | 				value.default(btn) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		get: function(field){  | 
					
						
							|  |  |  | 			return $(field).attr('state') | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * Constructors... | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Show a complex form dialog
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will build a form and collect it's data on "accept" specified by
 | 
					
						
							|  |  |  | // the config object...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // config format:
 | 
					
						
							|  |  |  | //	{
 | 
					
						
							|  |  |  | //		// simple field...
 | 
					
						
							|  |  |  | //		<field-description>: <default-value>,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		...
 | 
					
						
							|  |  |  | //	}	
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // <field-description> and split in two with a "|" the section before will
 | 
					
						
							|  |  |  | // show as the field text and the text after as the tooltip.
 | 
					
						
							|  |  |  | // Example:
 | 
					
						
							|  |  |  | // 		"field text | field tooltip..."
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // field's default value determines it's type:
 | 
					
						
							|  |  |  | // 	bool		- checkbox
 | 
					
						
							|  |  |  | // 	string		- textarea
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // see FIELD_TYPES for supported field types.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: if btn is set to false explicitly then no button will be 
 | 
					
						
							|  |  |  | // 		rendered in the form dialog.
 | 
					
						
							|  |  |  | // NOTE: to include a literal "|" in <field-description> just escape it
 | 
					
						
							|  |  |  | // 		like this: "\|"
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX add form testing...
 | 
					
						
							|  |  |  | // XXX add undefined field handling/reporting...
 | 
					
						
							|  |  |  | function formDialog(root, message, config, btn, cls){ | 
					
						
							|  |  |  | 	cls = cls == null ? '' : cls | 
					
						
							|  |  |  | 	btn = btn == null ? 'OK' : btn | 
					
						
							|  |  |  | 	root = root == null ? $('.viewer') : root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var form = $('<div class="form"/>') | 
					
						
							|  |  |  | 	var data = {} | 
					
						
							|  |  |  | 	var res = $.Deferred() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// handle message and btn...
 | 
					
						
							|  |  |  | 	if(message.trim().length > 0){ | 
					
						
							|  |  |  | 		setTextWithTooltip(message, $('<div class="text"/>')) | 
					
						
							|  |  |  | 			.appendTo(form) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// build the form...
 | 
					
						
							|  |  |  | 	for(var t in config){ | 
					
						
							|  |  |  | 		var did_handling = false | 
					
						
							|  |  |  | 		for(var f in FIELD_TYPES){ | 
					
						
							|  |  |  | 			if(FIELD_TYPES[f].test(config[t])){ | 
					
						
							|  |  |  | 				var field = FIELD_TYPES[f] | 
					
						
							|  |  |  | 				var html = $(field.html) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// setup text and data...
 | 
					
						
							|  |  |  | 				setTextWithTooltip(t, html.find('.text'), html) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(field.set != null){ | 
					
						
							|  |  |  | 					field.set(html, config[t]) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(field.get != null){ | 
					
						
							|  |  |  | 					// NOTE: this is here to isolate t and field.get values...
 | 
					
						
							|  |  |  | 					// 		...is there a better way???
 | 
					
						
							|  |  |  | 					var _ = (function(title, getter){ | 
					
						
							|  |  |  | 						html.on('resolve', function(evt, e){ | 
					
						
							|  |  |  | 							data[title] = getter(e) | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 					})(t, field.get) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				form.append(html) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				did_handling = true | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// handle unresolved fields...
 | 
					
						
							|  |  |  | 		if(!did_handling){ | 
					
						
							|  |  |  | 			console.warn('formDialog: not all fields understood.') | 
					
						
							|  |  |  | 			// XXX skipping field...
 | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add button...
 | 
					
						
							|  |  |  | 	if(btn !== false){ | 
					
						
							|  |  |  | 		var button = $('<button class="accept">'+btn+'</button>') | 
					
						
							|  |  |  | 		form.append(button) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		var button = null | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var overlay = showInOverlay(root, form) | 
					
						
							|  |  |  | 		.addClass('dialog ' + cls) | 
					
						
							|  |  |  | 		.on('accept', function(){ | 
					
						
							|  |  |  | 			form.find('.field').each(function(_, e){ | 
					
						
							|  |  |  | 				$(e).trigger('resolve', [$(e)]) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// XXX test if all required stuff is filled...
 | 
					
						
							|  |  |  | 			res.resolve(data, form) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			hideOverlay(root) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		.on('close', function(){ | 
					
						
							|  |  |  | 			res.reject() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(button != null){ | 
					
						
							|  |  |  | 		button.click(function(){ | 
					
						
							|  |  |  | 			overlay.trigger('accept') | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// focus an element...
 | 
					
						
							|  |  |  | 	// NOTE: if first element is a radio button set, focus the checked
 | 
					
						
							|  |  |  | 	//		element, else focus the first input...
 | 
					
						
							|  |  |  | 	form.ready(function(){  | 
					
						
							|  |  |  | 		// NOTE: we are using a timeout to avoid the user input that opened
 | 
					
						
							|  |  |  | 		// 		the dialog to end up in the first field...
 | 
					
						
							|  |  |  | 		setTimeout(function(){ | 
					
						
							|  |  |  | 			var elem = form.find('.field input').first() | 
					
						
							|  |  |  | 			if(elem.attr('type') == 'radio'){ | 
					
						
							|  |  |  | 				form.find('.field input:checked') | 
					
						
							|  |  |  | 					.focus() | 
					
						
							|  |  |  | 					.select() | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				elem | 
					
						
							|  |  |  | 					.focus() | 
					
						
							|  |  |  | 					.select() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}, 100) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /************************************************ Standard dialogs ***/ | 
					
						
							|  |  |  | // NOTE: these return a deferred that will reflect the state of the 
 | 
					
						
							|  |  |  | // 		dialog, and the progress of the operations that it riggers...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX might be a good idea to be able to block the ui (overlay + progress
 | 
					
						
							|  |  |  | // 		bar?) until some long/critical operations finish, to prevent the
 | 
					
						
							|  |  |  | // 		user from breaking things while the ui is inconsistent...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function alertDialog(){ | 
					
						
							|  |  |  | 	var message = $.makeArray(arguments).join(' ') | 
					
						
							|  |  |  | 	return formDialog(null, String(message), {}, false, 'alert') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function promptDialog(message, dfl, btn){ | 
					
						
							|  |  |  | 	btn = btn == null ? 'OK' : btn | 
					
						
							|  |  |  | 	var res = $.Deferred() | 
					
						
							|  |  |  | 	formDialog(null, message, {'': ''+(dfl == null ? '' : dfl)}, btn, 'prompt') | 
					
						
							|  |  |  | 		.done(function(data){ res.resolve(data['']) }) | 
					
						
							|  |  |  | 		.fail(function(){ res.reject() }) | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | function confirmDialog(){ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                                                */ |