/*
Cross-browser prompt class.
Copyright 2007-2008 Craig Manley.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*
Id: Prompt.js,v 1.7 2008/09/05 00:57:31 cmanley Exp

This class has been created as a work-around for the MSIE 7 default behaviour of suppressing the javascript prompt.
This has broken millions of web sites. Work-arounds to this problem can be any of these:
 1. Use the showModalDialog method instead and thereby only support MSIE browsers (they wish).
 2. Do away with using prompt dialogs completely.
 3. Use simulated modal dialogs with layer's and key capturing (complicated and messy).
 4. Use this class to simulate the javascript prompt function by using showModalDialog for MSIE 7 and up.

Usage:
1. Place the files Prompt.js, Prompt.html, and Prompt.png together into any directory in your site.
   Do not rename any of the files or else it won't work.
2. Place this line in the head section of your html:
   <script type="text/javascript" src="/path/to/Prompt.js"></script>
3. Call the Prompt.show method where you would normally call the javascript prompt function:
   var name  = Prompt.show('Who are you?');
   var sides = Prompt.show("How many sides does a square have?", 4, 'Silly question dialog');
   var count = Prompt.show("How many\nlines\ndo\n\n\nyou see\nhere?", '', 'Answer this');

Extra feature(s):
  For browsers that support the showModalDialog method:
    The second argument of Prompt.show doesn't have to be a string, but may be an array of values to display in a
    select element for the user to choose from. The selectedIndex will be returned if the user presses the OK button.
    You may also pass the index or indici to pre-select using the 4th argument:
    	If this argument is an integer, then a select-single element is used and the result is an integer (the selected index).
    	If this argument is an array, then a select-multi element is used and the result is an array (of selected indici).
    Example single:
    	var colors = ['','red','blue','green','yellow'];
    	var i = Prompt.show("Choose your favourite color:", colors, 'Color dialog', 0);
    	if (typeof(i) == 'number') {
    		alert('Your color is: ' + colors[i]);
    	}
    	else {
    		alert('You cancelled the dialog.');
    	}
    Example multi:
		var colors = ['','red','blue','green','yellow'];
		var a = Prompt.show("Choose your favourite colors:", colors, 'Color dialog', []);
		if (a == null) {
			alert('You cancelled the dialog.');
		}
		else {
			var chosen = [];
			for (var i=0; i<a.length; i++) {
				chosen.push(colors[a[i]]);
			}
			alert('Your colors are: ' + chosen.join(', '));
		}

*/


function Prompt() {

	/*** Private properties ***/
	var _msie = (navigator.appVersion.indexOf('MSIE') != -1) && (navigator.userAgent.indexOf('Opera') == -1);
	var _simulate = navigator.appVersion.match(/\bMSIE (\d+)/) && (RegExp.$1 >= 7) && window.showModalDialog;
	var _script_uri_path = (
		function(basename) {
			var tags = document.getElementsByTagName('script');
			var result = '';
			for (var i=0; i < tags.length; i++) {
				var src = tags[i].getAttribute('src');
				if (!src) {
					continue;
				}
				var x = src.lastIndexOf('/');
				if (x == src.length - 1) {
					continue;
				}
				if (basename != (x == -1 ? src : src.substr(x + 1))) {
					continue;
				}
				// found
				result = (x == -1) ? src : src.substr(0,x + 1);
				break;
			}
			return result;
		})('Prompt.js');


	/*** Private methods ***/

	// Returns the estimated dimensions of the given string when rendered as text (not html).
	// Functionally similar to the Windows API function GetTextExtent(h,txt).
	// 'opts' is an optional object in which you may pass styles such as 'fontFamily' or 'fontSize'.
	// EXPERIMENTAL:
	//   Requires more testing.
	//   Perhaps the 1st argument should be a DOM element to be cloned instead of using opts.
	var _getTextExtent = function(txt, opts) {
		var div = document.createElement('div');
		if (opts) {
			if (opts['fontFamily']) {
				div.style.fontFamily = 'font-family: ' + opts['fontFamily'];
			}
			if (opts['fontSize']) {
				div.style.fontSize = opts['fontSize'];
			}
		}
		div.style.position = 'absolute';
		div.style.top = 0;
		div.style.left = 0;
		div.style.whiteSpace = 'nowrap';
		div.style.overflow = 'hidden';
		div.zIndex = 6322;
		var lines;
		// MSIE bug (tested with MSIE 7) String.split() doesn't keep empty matches, so this hack is required:
		if (_msie) {
			lines = [];
			while (txt.length && txt.match(/^([^\r\n]*)(\r\n|\n\r|\r|\n)?/)) {
				lines.push(RegExp.$1);
				txt = txt.substr(RegExp.$2 ? RegExp.$1.length + RegExp.$2.length : RegExp.$1.length);
			}
		}
		else {
			lines = txt.split(/\r\n|\n\r|\r|\n/);
		}
		for (var i = 0; i < lines.length; i++) {
			div.appendChild(document.createTextNode(lines[i] == ' ' ? '' : lines[i]));
			if (i < lines.length - 1) {
				div.appendChild(document.createElement('br'));
			}
		}
		div.style.visibility = 'hidden';
		//div.style.backgroundColor = 'green';
		var container = document.body;
		if (container.firstChild) {
			container.insertBefore(div, container.firstChild);
		}
		else {
			container.appendChild(div);
		}
		var dims = [div.offsetWidth, div.offsetHeight];
		container.removeChild(div);
		div = null;
		return dims;
	};


	/*** Public methods ***/
	// Either calls or simulates the javascript prompt function.
	// The first 2 arguments are the same as for the javascript prompt function, but you may pass an array of input values for selection as the 2nd argument.
	// An optional 3rd argument can be passed which is the caption to be used
	// in the title bar, but this only applies to MSIE 7+.
	this.show = function(msg) {
		var argv = arguments;
		if (msg && (typeof(msg) == 'string')) {
			msg = msg.replace(/^\s+|\s+$/g,'');
		}
		var input = argv.length <= 1 ? '' : argv[1];
		var caption = argv.length <= 2 ? '' : argv[2];
		var selectedIndex = argv.length <= 3 ? -1 : argv[3];
		var result;
		if (_simulate || (window.showModalDialog && (typeof(input) == 'object') && (String(input.constructor) == String(Array)) && input.length)) {
			var dims = _getTextExtent(msg, {fontFamily: 'Arial, sans-serif', fontSize: '10pt'});
			var w = dims[0] + 340;
			if (w < 250) {
				w = 250;
			}
			var h = dims[1] + 115;
			if (!_msie) {
				h += 16; // Firefox 3 needs more height.
			}
			// Multi-select requires more height.
			if ((typeof(input) == 'object') && (String(input.constructor) == String(Array)) && input.length && (typeof(selectedIndex) == 'object') && (String(selectedIndex.constructor) == String(Array))) { // select-multi
				h += Math.round(16.4 * (Math.min(15, input.length) - 1));
			}
			result = window.showModalDialog(
				_script_uri_path + 'Prompt.html',
				{ msg: msg, input: input, caption: caption, selectedIndex: selectedIndex },
				'dialogWidth: ' + w + 'px; dialogHeight: ' + h + 'px');
		}
		else {
			if ((typeof(input) == 'object') && (String(input.constructor) == String(Array)) && input.length) {
				throw "Array type 'input' arguments not supported in this browser.";
			}
			result = prompt(msg, input);
		}
		return result;
	};
}
Prompt.show = function(msg) {
	var argv = arguments;
	var input = argv.length <= 1 ? '' : argv[1];
	var caption = argv.length <= 2 ? '' : argv[2];
	var selectedIndex = argv.length <= 3 ? -1 : argv[3];
	var p = new Prompt();
	return p.show(msg, input, caption, selectedIndex);
};

