/** MI.js **********************************************************************
 * @fileOverview
 * Creates an object that provides a namespaced environment for javascript code 
 * from McClatchy Interactive. This library requires jQuery to already be present.
 *
 * When naming new methods, start method names with an underscore if it is 
 * intended as a "private" method, i.e. not intended for direct reference
 * 
 * @minify true
 * @author Joe Whetzel (jwhetzel [at] mcclatchyinteractive.com)
 * @namespace mi
 */
// create the mi object, with a media domain value, if it doesn't already exist
var mi = (typeof mi == 'undefined') ? {'media_domain':''} : mi;




/* This method parses name=value argument pairs from
 * the query string of the URL. It stores the name=value pairs in 
 * properties of an object and returns that object.
 * Adapted from "Javascript: The Definitive Guide" by David Flanagan
 */
mi.getArgs = function() {
        if (typeof mi.args == 'undefined') {
	        mi.args = {};
	        var query = location.search.substring(1);
	        var pairs = query.split('&');
	        for(var i=pairs.length -1; i >= 0; i--) {
		        var pos = pairs[i].indexOf('=');
		        if (pos == -1) {continue;}
		        mi.args[pairs[i].substring(0,pos)] = unescape(pairs[i].substring(pos+1));
	        }
        }
        return mi.args;
};


/* A stand-in for console.log() for browsers without the functionality
 * The logged message is stored for later retreival. This function gets set as
 * console.log by mi.fixConsole if needed. Each logged message is separated by 
 * a line of hyphens.
 */
mi._console = function(s) {
	mi._console.log = (mi._console.log && mi._console.log.length > 0) ? mi._console.log + '\n---------------------------------------------------\n' + s : s;
};



/* goof-proof calls to the console -- IMMEDIATE EXECUTION HERE
 * Defines a console object (with "empty" methods) as needed; allows code in any browser to
 * call Firebug console methods without error. Doesn't overwrite existing console so as to not
 * interfere with Safari's console. The Firebug methods array should be maintained in sync with
 * the actual Firebug console.
 *
 * This method based on Pluck's NYX object method of same name.
 * 
 * TODO: this could be enhanced to make dir do something meaningful in the Safari console
 */
mi.fixConsole = function() {
	if (typeof window.console != "object") { window.console = {}; }
	if (window.console.is_fixed) {/*already fixed*/}
	else {
		// list of firebug method names, "log" should always be first
		// this list is used to create "stand-in" methods for the console object if needed
		var firebugMethods = ["log","debug","info","warn","error","assert","dir","dirxml",
			"trace","group","groupEnd","time","timeEnd","profile","profileEnd","count"];
		var methodCount = firebugMethods.length;
		var args = mi.getArgs();
		var view = (args.viewlog && args.viewlog == '1');
		for (var i = 0; i < methodCount; i++) {
			var methodName = firebugMethods[i];
			if (typeof window.console[methodName] != "function") {
				switch (methodName) {
					// Firebug console methods can be replicated here by adding cases
					case 'log':
						if (view) {
							window.console.log = mi._console;
							if (window.addEventListener) {
								window.addEventListener("load", function(){alert(mi._console.log);}, false);
							} else if (window.attachEvent) {
								window.attachEvent("onload", function(){alert(mi._console.log);});
							}
							//window.jQuery(window).bind('load',function(){alert(mi._console);});
						} else {
							window.console.log = function(){};
						}
						break;
					default:
						eval("window.console[methodName] = function(s){window.console.log('"+methodName.toUpperCase() + ": '+ s)};");
				}
			}
		}
	}
	//add our tracking flag
	window.console.is_fixed = true;
};
mi.fixConsole();

/* handy constructor for cloning objects
 * by default, setting a variable equal to a pre-existing object just creates
 * a reference to the original, this allows you to create an independant copy
 * of the original with no back-reference
 */
mi.cloneObject = function(sourceObj) {
	for (i in sourceObj) {
		if (typeof sourceObj[i] == 'object') {
			this[i] = new mi.cloneObject(sourceObj[i]);
		} else {
			this[i] = sourceObj[i];
		}
	}
};


/* a standard constructor for application objects within the mi name space
 * optionally pass an object to automatically load the configuration values
 */
mi.App = function() {
	var _configs = {};
	this._manageConf = function(prop, val) { return val; };
	this.setConf = function() {
		switch (arguments.length) {
			case 1:
				for (var prop in arguments[0]) {
					_configs[prop] = this._manageConf(prop, arguments[0][prop]);
				}
				break;
			case 2:
				_configs[arguments[0]] = this._manageConf(arguments[0],arguments[1]);
				break;
		}
	};
	this.getConf = function(prop) {
		return _configs[prop];
	};
	this.viewConfs = function() {
		console.dir(_configs);
	};
	this.cache = {};
	switch (arguments.length) {
		case 1:
			this.setConf(arguments[0]);
			break;
		case 2:
			this.setConf(arguments[0], arguments[1]);
			break;
	}
};


/* method for discovering the object/node that kicked off the current event
 */
mi.getEventSrc = function (e) {
	if (!e) {e = window.event;}
	if (e.target) {
		return e.target;
	} else if (e.srcElement) {
		return e.srcElement;
	}
};


/* method for parsing a template and replacing a pattern with the equivalent
 * attributes from an object
 *
 * var data object to get values from
 * var template string containing placeholders
 *
 * placeholders in the template should be given the name of the attribute to be 
 * used as the substitute surrounded by "@" symbols, i.e. @name@
 *
 * the pattern is defined outside of the method to avoid instantiating the 
 * pattern every time the method is used
 */
mi.templateVarPattern = /\@([^\@]+)\@/g;
mi.templateParser = function(data, template) {
	return template.replace(mi.templateVarPattern, function() {
			return data[arguments[1]];
		}
	)
};


/* method for parsing name/value data into name/value pairs
 *
 * expects three arguments: sourceData, firstDelimiter, secondDelimiter.
 */
mi.makeHash = function (sourceData, firstDelimiter, secondDelimiter) {
	if (sourceData && firstDelimiter && secondDelimiter) {
        	var hash = {};
        	var pairs = sourceData.split(firstDelimiter);
        	var pos; 
        	for(var i=pairs.length -1; i >= 0; i--) {
			if (typeof(pairs[i + 1]) != 'undefined') {
                		pos = pairs[i].indexOf(secondDelimiter);
                		if (pos == -1) {continue;}
                		hash[pairs[i].substring(0,pos)] = pairs[i].substring(pos+1);
                	}
        	}
        	return hash;
	}
        else {
		console.log('sourceData, firstDelimiter, & secondDelimiter must be defined. There are no default values.');
	}
};




/** MI.js ^ ***************************************************************** */

/** MI_Search.js ***************************************************************
 * @fileOverview
 * App used to provide function behind search options within the standard search
 * widget.
 *
 * @minify true
 * @author Jamison Kirk (jkirk [at] mcclatchyinteractive.com)
 */


mi.Search = function() {
        mi.App.apply(this, arguments);   // makes this a sub-class of the mi.App class
        mi.getArgs();
        this.kill;
};

//  called from form onsubmit
//  uses option/radio setting to determine which URL to build and then calls the appropriate method
mi.Search.prototype.submitForm = function() {
        this.kill = "false";

        switch (this.getConf("searchSelectorType")) {
                case "option" :
                        var searchType = document.miSearchForm.aff.value;
                break;
                case "radio" :
                        var searchType = $('input:radio[name=aff]:checked').val();
                break;
        }
        var searchText = encodeURIComponent(document.miSearchForm.keywords.value);
        if (searchType == parseInt(searchType)){
                return;
        }
        else {
                this.searchParamConfig(searchType, searchText);  //build configuration object
                this.buildForm(searchType);                              //create the form input elemnets
        }
        // if no config case present in affiliates configuration file, 
        if (this.kill == "false") {
                document.miSearchForm.submit();
        }
        else {
                return false;
        }
};


//  removes current hidden input elements and adds new ones based on configuration "query_fields"
mi.Search.prototype.buildForm = function(search_type) {
        var self = this;

        // if the #searchInputContainer div contains data via innerHTML then proceed into JQuery.
        // That simple test decreases obtrusive overhead of jquery processes when unnecesary which
        // happens to be most of the time. But when the condition is met removing any unneeded input
        // elements is essential to reliable search execution.
        // The following .remove() removes hidden input elements from the page. Although the
        // #searchInputContainer div is placed only around the hidden input elements, thus only those
        // being affected by the .remove, the jquery also limits based on type="hidden" in case the
        // container div encompases other type input elements
        var searchInputContainer_div = document.getElementById("searchInputContainer").innerHTML;
        if (searchInputContainer_div) {
                $("#searchInputContainer > input[type='hidden']").each(function(){
                        $(this).remove(); //  remove input
                });
        }

        // creates input elements using getConf method from siteConfig file
        // In any case that the buildform method is executed the following jquery must be executed as
        // there are input elements to be appended. In the case that the config hasn't been properly
        // set up with params and values the error thrown will be caught.
        try{
                jQuery.each(self.getConf("query_fields"), function(paramName, paramValue) {
                        paramName = paramName.replace(/(.*)_mihyphen_(.*)/, "$1-$2");

                        $("<input type='hidden' name='" + paramName + "' value='" + paramValue + "' />").appendTo("#searchInputContainer");
                });
        }
        catch (e) {
                console.error("Script Caught Error - " + e);
        }

        document.miSearchForm.action = self.getConf("form_action");  //set action using getConf method from siteConfig file
};


// if the search results site honors the search query string we submit, this sets the option
// or radio button to the kind just searched on the search results page. 
mi.Search.prototype.checkOption = function() {
        var self = this;

        if (typeof mi.args.collection != "undefined") {
                switch (self.getConf("searchSelectorType")) {
                        case "option" :
                                if (mi.args.collection == "WEB"){
                                        $("select#search_select option[value='web_search']").attr("selected", 1);
                                } else if (mi.args.collection == "ARCHIVES") {
                                        $("select#search_select option[value='archives']").attr("selected", 1);
                                } else {
                                        $("select#search_select option[value='h_archives']").attr("selected", 1);
                                }
                        break;
                        case "radio" :
                                if (mi.args.collection == "WEB"){
                                        $("#search_web").attr("checked", 1);
                                } else if (mi.args.collection == "ARCHIVES") {
                                        $("#search_archives").attr("checked", 1);
                                } else {
                                        $("#search_history").attr("checked", 1);
                                }
                        break;
                }
        }
};

// called from each sites configuration file default case in the event that a 
// radio/option type has not been configured
mi.Search.prototype.configErrorReporter = function() {

        this.kill = "true";
        alert("Option doesn't exist in your configuration. Please review your browsers error console.");
        console.error("Option doesn't exist in your configuration. Please submit a ticket to MI Support for assistance.");
        return false;
}

/** MI_Search.js ^ ***************************************************************** */
/**
 * @fileOverview
 * This is the configuration file for the affiliate. Making changes to this file could result in
 * breaking your search. If you have any questions please submit a ticket via the Support Portal.
 *
 * When hyphens (-) are used in URL query params, the string "_mihyphen_" is used in place of an
 * actual hyphen (-) in the object property names below. Hyphens can't be used in object property
 * names.
 *
 *
 *
 * @minify true
 * @author Jamison Kirk (jkirk [at] mcclatchyinteractive.com)
 */


mi.Search.prototype.searchParamConfig = function(search_type, search_text) {

        this.setConf("searchSelectorType","option");

        if (search_type) {
                switch (search_type) {
                        case "web_search":

                                this.setConf("form_action","http://search.thesunnews.com/search-bin/search.pl.cgi");
                                this.setConf("query_fields",{sf_Keywords:search_text,
                                                                product:"Yahoo,Overture",
                                                                collection:"WEB",
                                                                live_template:"http://www.thesunnews.com/searchresults/v-ysr/index.html",
                                                                error_template:"http://www.thesunnews.com/searchresults/v-yerr/index.html",
                                                                preview_template:"http://preview.thesunnews.com/searchresults/v-ysr/index.html",
                                                                results_per_page:"10",
                                                                preview:"0",
                                                                prop_related:"1",
                                                                prop_dym:"1"}
                                                );
                        break;
                        case "archives":

                                this.setConf("form_action","http://www.newslibrary.com/nlsearch.asp?");
                                this.setConf("query_fields",{search_mode:"all",
                                                                date_mode:"year",
                                                                year:"last+180+days",
                                                                sort:"d%3Ah",
                                                                nitems:"10",
                                                                region:"MB",
                                                                dbquery:search_text,
                                                                collection:"ARCHIVES"}
                                                );
                        break;
                        default:
                                this.configErrorReporter();
                }
        }
};

