
//- Dialog ----------------------------------------------------------------
// •needs:	popup, jquery

jQuery(document).ready(function($){
// this only allows one dialog to be showing at a time: if another appears,
// the first is automatically closed/cancelled/answered ‘no’

popup_dialog = new Popup('dialog', 'poppedup');

popup_dialog.alert = function (m) { var _ = popup_dialog;
	_.type = 'alert';
	_.popup_html(m);
	return false;
}

popup_dialog.confirm = function (m, callback, y, n) { var _ = popup_dialog;
	_.type = 'confirm';
	y = (y == undefined ? "OK" : y);
	n = (n == undefined ? "Cancel" : n);
	_.callback = function(v){ _.hide(); callback(v) };
	_.popup_html(m + '<div class="popup-controls">'
	+ '<button name="Cancel" type="button" class="secondary">'+n+'</button>'
	+ '<button name="OK" type="button">'+y+'</button>'
	+ '</div>');
	var b = $("."+_.content_class);
	setTimeout(function(){
		$(b).find('[name=Cancel]').click(function(){_.callback(false)});
		$(b).find('[name=OK]').click(function(){_.callback(true)}).focus();
	}, 100);// delay so there’s time for the confirm to be created
	return false;
}

popup_dialog.password = function (m, signin) { var _ = popup_dialog;
	_.type = 'password';
	if (signin == undefined) signin = validate_and_sign_in;
	_.signin = function (f) {
		_.hide();
		with (f) signin(username.value, password.value);
	}
	_.bail = function () { _.hide() }
	_.popup_html('<form onsubmit="return false">'
	+ m + '<table><tr><th>Name</th><td><input name="username" /></td></tr>'
	+ '<tr><th>Password</th><td><input type="password" name="password" value="" />'
	+ '</td></tr></table><div class="popup-controls">'
	+ '<button name="Cancel" class="secondary" type="button">'+"Cancel"+'</button>'
	+ '<button name="OK" type="submit">'+"Sign In"+'</button>'
	+ '</div></form>');
	$(_).find('form').submit(function(){ _.signin(_); return false });
	$(_).find('[name="Cancel"]').click(function(){ _.bail() });
	return false;
}

popup_dialog.notify = function (m) { var _ = popup_dialog;
	_.type = 'notify';
	var original = { show: _.show, hide: _.hide };
	// popup w/ auto-popdown
	_.show = function(){
		_.show = original.show;
		_.show();
		var hide_notification = function () {
			with (_) if (type == 'notify') hide();
		};
		setTimeout(hide_notification, 2000);
	};
	_.popup_html(m);
	return false;
}



// change popup hide method so failed reloadable won’t open lefover content
var original_hide = popup_dialog.hide;
popup_dialog.hide = function(){ var _ = popup_dialog;
	$("."+_.content_class).html("");// Empty dialog
	if (_.reloadable_showing) _.reloadable_showing = undefined;// Drop state
	_.original_hide = original_hide;
	_.original_hide();
	_.original_hide = undefined;
}


// show url in current page w/ popup,
// options
//    bounce: if true, return to previous url after form submit, otherwise close
//   on_load: run on dialog content after load & link loads (not form submits)
// on_submit: run on dialog content just before form submit (for targetted forms)
// set_style: set included css on text/html content that’s loaded
// trigger_delay: delay before running triggers, default = _trigger_delay const
// show_while_sending: content to show while sending any ajax forms
popup_dialog.reloadable = function (url, options) { var _ = popup_dialog;
 var state = { url: url };

 // popuplate options
 switch (typeof options) {
	case 'boolean': state.options = { bounce: options }; break;
	case 'function': state.options = { on_load: options }; break;
	case 'object': state.options = options; break;
	case 'string': state.options = { set_style: options }; break;
	default: state.options = {};
 }
 if (typeof _.reloadable_options_adjust == 'function')// standard option adjusts
	state.options = _.reloadable_options_adjust(state.options);

 // popup was already showing a reloadable, so just load the new url
 var reloadable = _.reloadable_showing;
 if (reloadable && _.type == reloadable._type) {
	if (reloadable.state.url != state.url) {
		// load a new, different, url..
		reloadable.history.push(reloadable.state);//old state to history
		reloadable.state = state;// show new url
	}	
 }
 else {// popup was inactive or was not a reloadable
	_.reloadable_showing = new Reloadable(state);
	_.type = _.reloadable_showing._type;
 }

 // load reloadable’s content
 var us = _.reloadable_showing.us;
 // some Chrome releases run an ajax request (for the page’s original url)
 // at script parse time - checking for a real url blocks this
 if (state.url) $.ajax({ url: state.url, dataType: 'text',
  beforeSend: function(){// merge w/ form stuff in reloadable_associate???
	_.reloadable_showing.request_url = state.url;
	$(document.body).addClass(us._loading_class);
  },
  complete: function(request, status){
	return us.load_content(request.responseText, status);
  }
 });

 return false;
}


function Reloadable(state) {
 this.history = [];// prep history storage
 this.state = state;// show new url
 this.us = this;// save this object for future access
}


Reloadable.prototype.load_content = function(response, status){with(this){
 $(document.body).removeClass(us._loading_class);

 // some hosts don’t properly return error statuses, so populate it here
 if (!response || response.match(_empty_rx)) status = "error";

 // handle response//var type = request.getResponseHeader('Content-Type');
 if (status == "success") {
  if (response.match(/^\s*(?:<pre[^>]*>)?\s*{(?:.|\n)*}\s*(?:<\/pre>)?\s*$/)) {// json
  	//                 above check was: type.match("application/json")
	var response_json = us.clean_json(response);
	// parse to get orders object (!!!needs recovery on catch!)
	try { var orders = $.parseJSON(response_json) }
	catch (e) { if (typeof console != 'undefined') debugger;
		alert("The form submission’s response was badly formed.\n"+$('<div/>').text(e.toString()).html());// that div stuff at the end escapes html characters for display
		// fallback strategy???
	};
	// -- handle response ------------------------------------------
	// trigger event or action from orders (both???)
	//if (typeof console != 'undefined') debugger;
	var _data = orders.data ? orders.data : undefined;
	if (orders.event)
		$(document).trigger(orders.event, _data);
	else if (orders.action)
		orders.action(_data);
	// set popup up according to orders
	if (_.reloadable_showing.state.options.bounce) {
		//console.info("A bounce was requested");
		us.visit_previous_url();
	}
	else if (orders.alert)
		_.alert(orders.alert);
	else if (orders.notification)
		_.notify(orders.notification);
	else	_.hide();
  }
  else {//is text/html response, check was: if (type.match("^text/"))
	if (response.match(us._empty_rx))
		us.could_not_load(_.request_url);
	else
		_.popup_html(response);
	with (_.reloadable_showing.state) {
		$(document).trigger('popup-content-changed', us.content);
		us.content.reloadable_associate(options);// prep new content
		if (options.on_load)// run load trigger
			us.run_trigger(options.on_load, us.content);
		// set style, if requested (useful for form loads too???)
		options.set_style ? us.content.attr('style', options.set_style)
		                  : us.content.removeAttr('style');
	}
  }// ..handle successful response by type
 }
 else if (status == "error") us.could_not_load(_.request_url);
 else {
	var error_msg = "Unknown AJAX status “"+status+"”";
	if (typeof console != 'undefined') {
		console.info(error_msg);
		debugger;
	}
	us.could_not_load(_.request_url, "<em>"+error_msg+"</em>.");
 }

 // show the new content
 _.request_url = undefined;
 _.show();
}};


// -- constants ----------------------------------------------------------------
Reloadable.prototype._empty_rx = new RegExp(/^[ \t\r\n]*$/);
Reloadable.prototype._error_class = "dialog-error";
Reloadable.prototype._loading_class = "reloadable-loading";
Reloadable.prototype._trigger_delay = 500;// msec before trigger fx
Reloadable.prototype._type = 'reloadable';// identification

// -- vars ---------------------------------------------------------------------
Reloadable.prototype._ = popup_dialog;
Reloadable.prototype.content = $("."+popup_dialog.content_class);

// -- methods ------------------------------------------------------------------
Reloadable.prototype.clean_json = function (json) {
	// clean up from browsers that wrap text responses in <pre> tags
	json = json.replace(/^\s*<pre[^>]*>\s*/, "");
	json = json.replace(/\s*<\/pre>\s*$/, "");
	// when chrome adds pre tags, it also converts ALL > to &gt;, etc.
	if (json.match(/"\s*&lt;/) || json.match(/&gt;\s*"/)) {
		json = json.replace(/&lt;/g, "<");
		json = json.replace(/&gt;/g, ">");
	}
	// clean response for strict json parsers that disallow ' quotes
	var cleaned = $.trim(json.replace(/({\s*|["']\s*[:,]\s*)'((\\'|[^'])*[^\\])'/g, "$1<<<'>>>$2<<<'>>>").replace(/({\s*|>>>\s*[:,]\s*)'((\\'|[^'])*[^\\])'/g, "$1<<<'>>>$2<<<'>>>").replace(/({\s*|["']\s*[:,]\s*)'((\\'|[^'])*[^\\])'/g, "$1<<<'>>>$2<<<'>>>").replace(/([^\\])"((\\"|[^"])*[^\\])"/g, "$1<<<'>>>$2<<<'>>>").replace(/([^\\])"/g, '$1\\"').replace(/<<<'>>>/g, '"'));//
	// console.info("JSON was ‘"+json+"’, clean version is ‘"+cleaned+"’");
	return cleaned;
}

Reloadable.prototype.could_not_load = function (url, msg) {with(this){

}};

Reloadable.prototype.run_trigger = function(trigger, where){
	if (trigger && typeof trigger == 'function') {
		var delay = this._trigger_delay;
		if (typeof this.state.options.trigger_delay == 'number')
			delay = this.state.options.trigger_delay;
		setTimeout(function(){ where.__trigger = trigger;
		                       where.__trigger();
		                       where.__trigger = undefined }, delay);
	}
};

Reloadable.prototype.visit_previous_url = function(){with(this){
	state.url = undefined;
	var previous_state = history.pop();
	if (previous_state) with (previous_state)// problems when backtracking???
		url ? _.reloadable(url, options) : _.hide();// close w/ no url
	else	_.hide();
}};// !!! methods: make them local to reloadable, not the popup dialog?


// ------------------------------------------------------------------------------
$.fn.reloadable_associate = function(options){ var _ = popup_dialog;
 var us = _.reloadable_showing.us;

 // ordinary links: load the linked file in the reloadable dialog
 $(this).find('a:not(.'+us._error_class+' > a, .plain-display a)').click(function(){
	// make deep copy of options, just x = y is a copy-by-reference
	var bounceoptions = $.extend(true, {}, options);
	bounceoptions.bounce = (options.bounce || $(this).hasClass("bounce"));
	_.reloadable(this.href, bounceoptions);
	return false;
 });
 
 // if there’s a submit trigger, attach it to any form in the reloadable
 if (options.on_submit)
	$(this).find('form').submit(function(){
		return us.run_trigger(options.on_submit, this);
 	});

 // forms that don’t need to load a response into the reloadable have the
 //  target attribute: close down the popup when they’re submitted, let the
 //  browser handle the form submit normally
 $(this).find('form[target]').submit(function(){
	_.original_hide = original_hide;
	_.original_hide();
	_.original_hide = undefined;
	return true;
 });

 // forms that load a response into the reloadable use an AJAX submit handler
 $.ajaxSetup({ dataType: 'text' });
 $(this).find('form:not([target])').ajaxForm({ iframe: false,
  // for debugging
  //error: function(x, s, e) { console.info("Error: status "+s) },
  //success: function(d, s) { console.info("Success: data "+d+" status "+s) },
  beforeSend: function(x){// merge w/ load stuff in reloadable fx???
	_.reloadable_showing.request_url = this.url+"?"+this.data;// track url
	if (us.state.options.show_while_sending) {
		us.sending_shower = setTimeout(function(){
			_.popup_html(us.state.options.show_while_sending);
			us.sending_shower = undefined;
		}, us._trigger_delay);
	}
	$(document.body).addClass(us._loading_class);
  },
  // for POST forms that contain a file upload, copy all POST vars to GET
  //  (prevents PHP overlarge POST failures)
  beforeSerialize: function(f, o){// form and options
	var q = f.formSerialize();
	o['url'] = o['url']+"?"+q;
  },
  // ajax form submission has completed...
  complete: function(request, status) {
	if (us.sending_shower) {
		clearTimeout(us.sending_shower);
		us.sending_shower = undefined;
	}
	var _text = request.responseText;
	// browsers sometimes html encode the response when file uploads involved
	if (_text.match(/^\s*<pre>(?:\n|.)*&gt;(?:\n|.)*&lt;(?:\n|.)*<\/pre>/i))
		_text = _text.replace(/\s*<\/?pre>\s*/g, "").replace(/&gt;/g, ">").replace(/&lt;/g, "<");
	return _.reloadable_showing.load_content(_text, status);
  }
 });

 // Cancel buttons back out to the previous reloadable page, or close the popup
 $(this).find('[name="Cancel"]').click(function(){ us.visit_previous_url() });
};// $.fn.reloadable_associate

// ------------------------------------------------------------------------------


// replace default window methods with new dialog-based versions
//inside_alert = function(a){};
window._alert = window.alert;
window.builtin_alert = window.alert;
window.inside_alert = window.alert;

// ie needs these to override the window properties
window.alert = popup_dialog.alert;
window.confirm = popup_dialog.confirm;
window.password = popup_dialog.password;
window.notify = popup_dialog.notify;

// ..& needs this for popup_dialog to be available
window.popup_dialog = popup_dialog;

// inform page that the popup dialog is available
$(document).trigger('popup-dialog-available');
});

