﻿/*

customizations.js: Customize the prototype.js Ajax default options,
and provide other stuff for interacting with .NET

TODO:
	- remove cruft from non-hashtable days (marked below as obsolete)
	
Patches to be aware of:

	- prototype.js, line 1228, to fix random readyState bug here:
		http://prototype.lighthouseapp.com/projects/8886/tickets/189-respondtoreadystate-is-called-with-a-random-readystate


*/

Ajax.awaitingReauthenticate = false;

Ajax.Base.prototype.initialize = function(options) {
	this.options = {
		method:       'post',
		asynchronous: true,
		contentType:  'application/json',
		encoding:     'UTF-8',
		parameters:   '',
		evalJSON:     true,
		sanitizeJSON:  true,
		evalJS:       true,
		onFailure:    handleAjaxError,
		onComplete:   handleAjaxComplete.bind(this),
		passwordChangeCallback : null,
		formId: null,
		onReauthCallback: null,
		allowRequestOnAwaitingForReauthenticate: false
	};
	Object.extend(this.options, options || { });

	this.options.method = this.options.method.toLowerCase();
	if (Object.isString(this.options.parameters))
		this.options.parameters = this.options.parameters.toQueryParams();
		
	if (Ajax.awaitingReauthenticate && !this.options.allowRequestOnAwaitingForReauthenticate) {
		this.request = function() {
			console.log("ignoring request");
		}
	}
};


String.prototype.evalJSON = function(sanitize) {
	var json = this.unfilterJSON();
	try {
		if (!sanitize || json.isJSON()) return unpackDictionary(stripD(eval('(' + json + ')'))); // Temporary fix to add Key/Value properties to an Array. TODO: Fix server-side.
	} catch (e) { }
	throw new SyntaxError(lpack.IHasBadJson + this.inspect());
}


// Add $JSON alias for convenience when serializing objects
var $JSON = Object.toJSON;

function stripD( json ) {
	try {
		var stripped = json["d"];
		if( stripped ) return stripped;
		else return json;
	} catch (e) { return json }
}


/* library function, etc. */

function showAjaxError( errorText )
{
	alert( "error: " + errorText.replace( /\\u0027/, "\n" ) );
}
function handleAjaxError( transport )
{
	if(transport.status == 401) {
		console.log("need reauth");
		if (typeof transport.request.options.onReauthCallback == "undefined" || transport.request.options.onReauthCallback == null) {
			window.location.reload(true);
		}
		else {
			Avenet.Utilities.reAuthenticate(transport.request.options.onReauthCallback);
		}
	} else if( transport.responseText.isJSON() )
		showAjaxError(transport.responseText.evalJSON(true).Message);
	else
	{
		var mess = transport.responseText;
		if( mess.indexOf("<html>") > -1 )
		{
			var errorWindow = window.open("");
			errorWindow.document.write(mess);
			errorWindow.document.close();
		}
		else showAjaxError(transport.responseText);
	}
}

var noPasswordResetForm = false;

function handleAjaxComplete( transport )
{
	if(transport.status == 0) {
		Avenet.Forms.destroyLoadingGraphic();
		console.log("broken connection, popping up");
		var connectionBrokenSheet = $('connectionBrokenSheet');
		if( connectionBrokenSheet && connectionBrokenSheet.controller ) {
			connectionBrokenSheet.controller.show();
		} else {
			if(confirm(lpack.ConnectionLost)) {
				window.location.reload();
			}
		}
	}
	// if the request has completed OK
	else if(transport.status == 200)
	{
		var response = transport.responseText.evalJSON(true);
		if(response.Status != null && !response.Status) //handle display errors here.
		{
			var errors = response.ErrorMessages;
			
			if ($H(errors).keys().length) {
				var message = ""
				$H(errors).each(function (err) {
					message += ('*' + err[0] + ': ' + err[1] + '<br />');
				});
				
				if( Avenet && Avenet.Forms ) Avenet.Forms.Validation.displayServerSideErrors( errors, this.options.formId );
				else if($("formErrors")) $("formErrors").update( message );
			}
		}
		
		/* handle forced change of password */
		if(response.ForcePasswordChange)
		{
			if (!this.options.passwordChangeCallback) {
				if (!noPasswordResetForm)
					Avenet.Utilities.doPasswordReset();
			}
			else {
				this.options.passwordChangeCallback();
			}
		}
	}
}

function changePassword() { 
	if(requireSSL && baseUrl.substring(8, 0) != 'https://') {
		new Ajax.Request(basePath + 'Services/SecurityService.svc/GetSecureRequest',{
			asynchronous: false,
			onSuccess: function (transport) {
				var response = transport.responseText.evalJSON(true);
				if(response.Status == true) {
					var url = window.location.host;
					new Ajax.Request(secureUrl + 'Scripts/SecureRequest.js',{
						method: 'get',
						crossSite: true,
						parameters: {r: response.ResponseObject, url: url, Action: 'ChangePassword', Hash: Object.toJSON(this.e.form.serialize( true ))},
						onSuccess: function (transport) {
							if(secureResponse.Status == true) {
								new Ajax.Request(basePath + 'Services/SecurityService.svc/FinishSecureRequest',{
									asynchronous: false,
									allowRequestOnAwaitingForReauthenticate: true,
									postBody: Object.toJSON({action: 'ChangePassword'}),
									onSuccess: function (transport) {
										Avenet.Forms.clearModalForm();
										Avenet.Utilities.forcePasswordChange = false;
									}.bind(this)
								});
							}
							//the password has been reset.
						},
						onFailure: function() {
							if(secureResponse.Status != null && !secureResponse.Status) //handle display errors here.
							{
								var errors = unpackDictionary(secureResponse.ErrorMessages);
								
								if ($H(errors).keys().length) {
									var message = ""
									$H(errors).each(function (err) {
										message += ('*' + err[0] + ': ' + err[1] + '<br />');
									});
									
									if( Avenet && Avenet.Forms ) Avenet.Forms.Validation.displayServerSideErrors( errors, 'ChangePassword' );
									else if($("formErrors")) $("formErrors").update( message );
								}
							}
						}
					});
				}
			}.bind(this)
		});
	} else {
		new Ajax.Request(basePath + 'Services/SecurityService.svc/ChangePassword',{
			postBody: Object.toJSON({hash: this.e.form.serialize( true )}),
			onSuccess: function ( transport ) {
				response = transport.responseText.evalJSON(true);                            
				if(response.Status == true) {
					Avenet.Forms.clearModalForm();
					Avenet.Utilities.forcePasswordChange = false;
				}
				//the password has been reset.
			}
		});
	}
}

Form.serializeElements = function(elements, options) {

    // save out contents of magic boxen to original field
    if (tinyMCE) tinyMCE.triggerSave();

    if (typeof options != 'object') options = { hash: !!options };
    else if (options.hash === undefined) options.hash = true;
    var key, value, submitted = false, submit = options.submit;


    var data = elements.inject({}, function(result, element) {
        if (!element.disabled && element.name) {
            key = element.name; value = $(element).getValue();
            if (value != null && (element.type != 'submit' || (!submitted &&
				submit !== false && (!submit || key == submit) && (submitted = true)))) {
                if (element.hasClassName('magicBox')) {
                    var ed = tinyMCE.get(element.id);
                    if (ed) {
                        value = ed.getContent();
                        if (value.strip() == '<p>&nbsp;</p>') value = '';
                        result[key] = value;
                    }
                } else if (element.type.match(/checkbox|radio|select-one/)) {
                    if (key in result) result[key] += ',' + value;
                    else result[key] = value
                }
                else {
                    //alert(value);
                    if (value.strip() == '<p>&nbsp;</p>') value = '';
                    result[key] = value;


                }
            }
        }
        return result;
    });
    var formContainer = null;
    if (elements.length > 0)
        formContainer = elements[0].up('form').parentNode;
    else {
        formContainer = $('buildCenterColumn').down('form').parentNode; //added this for evaluations that contain only link builders.
       // data = [];
    }



    //data.ContentFeedback = [];

    //serialize the link builders in here.
    formContainer.select('.linkbuilder.sortable').each(function(lb) {
        var varName = lb.id.split('_')[1];
        var values = "";
        var sortOrder = 0;

        lb.select('.adminContent').each(function(lbItem) {
            if (values.length) values += ",";

            var type;
            lbItem.className.split(' ').each(function(c) {
                var s = c.split('_');
                if (s.length > 1 && s[0] == 'type')
                    type = s[1].toLowerCase();
            } .bind(this));

            if (type != 'collector' && type != 'page')
                type = 'content';

            if (type == 'collector') {
                var items = lbItem.id.split('_');
                if (items.length == 3 && items[2] == '') {
                    return;
                }
            }

            values += type + ';' + lbItem.id + ';' + sortOrder;
            ++sortOrder;
        });
        data[varName] = values;
    });

    return packDictionary(data); // Temporary fix to add Key/Value properties to an Array. TODO: Fix server-side.
};

/*
patch to Prototype, apparently needed in IE7
to make Ext and Prototype play nice in that browser
*/

if(window.Event) {
	Object.extend(Event,{
		element: function(event) {
		  var node = Event.extend(event).target;
		  return node && Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
		},
 
		pointer: function(event) {
		  return {
			x: event.pageX || (event.clientX +
			  ((document && document.documentElement && document.documentElement.scrollLeft)
				  || (document && document.body && document.body.scrollLeft))),
			y: event.pageY || (event.clientY +
			  ((document && document.documentElement && document.documentElement.scrollTop)
				  || (document && document.body && document.body.scrollTop)))
		  };
		}
	});
}



//  Add rest() method to all functions, for use in preventing double-click and other such stuffs

Object.extend( Function.prototype, {
	
	// rest method.  you optionally pass it a "time to rest" value (defaults to 2sec).
	// use it like this: Event.observe( myThing, 'click', myFunction.bind(this).rest(1250) )
	rest: function( restTime ) {
		if( !restTime ) restTime = 1000;

		return this.wrap( function(proceed) {
			var callTime = (new Date()).getTime();
			
			if( this.lastCall && ((callTime-restTime) < this.lastCall) ) {
				// called too soon, do not want
				console.log("DENIED!!");
			} else {
				// called first time, we're ok
				this.lastCall = callTime;
				var args = $A( arguments );
				args.splice( 0, 1 );
				proceed( args );
			}
		}.bind(this));
	}
});


Ajax.Request.prototype.request = Ajax.Request.prototype.request.wrap( function(proceed, url) {
	var payload;
	
	if( this.options.postBody ) {
		this.options.postBody = this.options.postBody.substring(0, this.options.postBody.length - 1) + ",\"cookies\":\"" + escape(document.cookie) + "\"}";
	}
	else {
		this.options.postBody = "{ \"cookies\": \"" + escape(document.cookie) + "\"}";
	}

	proceed(url);
});

// patch to Prototype to correct the Position.within() method
Position.within = function(element, x, y) {
	if (this.includeScrollOffsets)
	  return this.withinIncludingScrolloffsets(element, x, y);
	this.xcomp = x;
	this.ycomp = y;
	this.offset = Element.viewportOffset(element);

	return (y >= this.offset[1] &&
			y <  this.offset[1] + element.offsetHeight &&
			x >= this.offset[0] &&
			x <  this.offset[0] + element.offsetWidth);
  }

// Temporary function that converts a hash into a IDictionary compatible format. TODO: Fix server-side.
function packDictionary(hash) {
	var newHash = [];
	if(hash != null && hash.constructor == Object) {
		$H(hash).each(function(item) {
			newHash.push({'Key' : item.key, 'Value' : item.value});
		});
	} else
		newHash = hash;
	return newHash;
}

// work around a WebKit + prototype bug
// see: http://prototype.lighthouseapp.com/projects/8886/tickets/501-safari-2-issues-with-findelements-in-prototype-1603
Selector.addMethods({
	shouldUseSelectorsAPI: function(){ return false; }
});

// Temporary function that converts a IDictionary serialized JSON object into a standard JSON hash. TODO: Fix server-side.
function unpackDictionary(hash) {
	return scrubWCFDictionaries(hash);
}

// new, more robust recursion-based unpackDictionary function, needed because the old one
// was freaking out in IE in some cases

function scrubWCFDictionaries( prop ){
	if(prop == null) {
		return prop;
	} else if( Object.isArray(prop) ) {
		if( isWCFArray(prop) ) {
			var cleanHash = {};
			$H(prop).each(function(i){
				if( i.value.Value ) cleanHash[i.value.Key] = scrubWCFDictionaries( i.value.Value );
				if( i.value.value ) cleanHash[i.value.key] = scrubWCFDictionaries( i.value.value );
			});
			
			return cleanHash;
		} else {
			var cleanArray = [];
			prop.each(function(i){ cleanArray.push( scrubWCFDictionaries(i) ); });
			return cleanArray;
		}
	} else if( typeof(prop) == "object" ) {
		var cleanHash = {};
		$H(prop).each(function(i){ cleanHash[i.key] = scrubWCFDictionaries( i.value ); });
		return cleanHash;
	} else {
		return prop;
	}
}

function isWCFArray( arr ){
	if( typeof(arr[0]) == 'object' && arr[0]  && ("key" in arr[0] || "Key" in arr[0]) && ("Value" in arr[0] || "value" in arr[0]))
	//if( arr[0]  && (arr[0]['key'] || arr[0]['Key']) && (arr[0]['Value'] ||  arr[0]['value']))
		return true;
	else
		return false;
}

function isBrowserCompatible() {
	if(!navigator.cookieEnabled) {
		alert('cookies are disabled. please enable them.');
	}
	
	//console.log(navigator.appName);
	//console.log(navigator.appVersion);
}
isBrowserCompatible();

//This function has a sister function in the Web/Text/Code/Utilities/CustomFormActions.cs class
// both must be maintained together to match
function formCustomActions() {
	$$("form").each(function(f) {
		// if the main div has this class on it, then we have a dropdown that will determine text placed in a text field
		// Dropdown to make selection: textSelect
		// Textblock to pull text from: {selectedItemId}LabelFieldText
		// Text field to enter text into: labelField
		if (f.down('div.customDropSelectsTextForLabelFieldForm')) {
			var select = $(f.id + '_textSelect');
			var selValue = select.options[select.selectedIndex].id.replace(f.id + '_', '');
			
			var block = $(f.id + '_' + selValue + 'LabelFieldText');
			if (block) {
				var text = '';
				if (block.firstChild.nodeValue)	{text = block.firstChild.nodeValue; console.log('firstChild.nodeValue');}
				else if (block.firstChild.data)	{text = block.firstChild.data; console.log('firstChild.data');}
				else if (block.textContent)		{text = block.textContent; console.log('textContent');}
				else if (block.innerText)		{text = block.innerText; console.log('innerText');}
				else if (block.innerHTML)		{text = block.innerHTML; console.log('innerHTML');}
				$(f.id + '_labelField').value = text;
			}
		}
	}.bind(this));
}
