/** 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(searchType) {
        this.kill = "false";

        switch (this.getConf("searchSelectorType")) {
                case "option" :
                        searchType = document.miSearchForm.aff.value;
                break;
                case "radio" :
                        searchType = $('input:radio[name=aff]:checked').val();
                break;
        }
        var searchText = 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
                });
        }

        //assign site config value "post" or "get" to method attribute of form
        $("#search_widget_form").attr('method', self.getConf("form_method"));

//        if (self.getConf("form_method")) {
//                $("#search_widget_form").attr('method', self.getConf("form_method"));
//        }

        // 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;
                }
        }



};

// drives dropdown functionality for sites not using radio buttons nor selects
mi.Search.prototype.dropDownSelection = function(target) {

                mi_search_type = target.children('a').attr("id");

                if ( target.children('a').is('#site_search') ) {

                        var this_image = target.find("img").attr("src");
                        mi.search.getConf("mi_search_widget_icon").attr("src", this_image);
                }
                else if ( target.children('a').is('#web_search') ) {

                        var this_image = target.find("img").attr("src");
                        mi.search.getConf("mi_search_widget_icon").attr("src", this_image);
                }
                else if ( target.children('a').is('#archives') ) {

                        var this_image = target.find("img").attr("src");
                        mi.search.getConf("mi_search_widget_icon").attr("src", this_image);
                }
                $("#search_keywords").focus();
                return false;

}


// 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 ^ ***************************************************************** */

// *****************************************************************************
// Function:	fetchKeywordUrlMap( 'myTargetSelector' )
// Arguments:	myKeywordUrlMap:  A string of URL to keyword mappings
//		myTargetSelector: JQuery style selector to inject keyword
//		mapping into.
// Purpose:	Based on keywords extrapolated from the current URL will compare
//		these keywords to a user generated mapping of URLs to Keywords
//		and if matched will output the URL link passed.
// Return:	NA
// *****************************************************************************
mi.Search.prototype.fetchKeywordUrlMap = function( myKeywordUrlMap, myTargetSelector ){

    // If 'myKeywordUrlMap' has a trailing '++' then we need to strip this, the
    //   '++' is replaced by Template Toolkit for every line break, and having
    //   a trailing '++' means the page element had a trailing line break with
    //   no data after it
    if( myKeywordUrlMap.match( /\++$/ ) )
	myKeywordUrlMap = myKeywordUrlMap.replace( /\+*$/, '' );
	
    // This will contain all the HTML to be injected into the selector passed
    //   above after processing.
    var formattedOutput		= '';
    // All the keywords extrapolated from the current URL, urlKeyword == Array
    var urlKeywords		= this.fetch404Keywords( );
    
    // This array will house all objects of class type keywordUrlMapClass
    var keywordUrlMapObjects	= [];
    
    // Now we have to parse the Keyword -> URL mappings so we can match on the
    //   404 keywords found.
    myKeywordUrlMap 	= myKeywordUrlMap.split( '++' );
    for( var i in myKeywordUrlMap ){
	// Example Map: Link Name 1||http://www.link1.com||link1, test1, keyword
	
	// Split the current keyword / url map by '||' and create new object
	var currentKeywordUrlMap	= myKeywordUrlMap[i].split( '||' );
	// Create the object and set the name and URL
	keywordUrlMapObjects[i]	= new this.keywordUrlMapClass( currentKeywordUrlMap[0], currentKeywordUrlMap[1] );
	
	// Now split the 3rd( [2] ) part of data by ',' and add to list of
	//   keywords for this object
	var currentKeywords		= currentKeywordUrlMap[2].split( ',' );
	for( var x in currentKeywords ){
	    keywordUrlMapObjects[i].addKeyword( currentKeywords[x] );
	}
    }
    
    
    // Finally loop through all the 404 keywords extrapolated, and call the
    //   keywordUrlMapClass objects 'matchKeyword' method to see if any of the
    //   objects keywords match the 404 keyword
    for( var i in urlKeywords ){	
	for( var x in keywordUrlMapObjects ){
	    if( keywordUrlMapObjects[x].matchKeyword( urlKeywords[i] ) ){
		// Then add the output code
		formattedOutput += "\
		    <li><a href='" + keywordUrlMapObjects[x].url + "'>" +
			    keywordUrlMapObjects[x].name + "</a>\
		    </li>";
	    }
	}
    }
    
    // Output the final HTML to the page
    $( myTargetSelector ).append( formattedOutput );
    
    
}

/**
 * Construct a keywordUrlMapClass
 * @class Basic class to house keyword to url mappings, and any helper methods
 * needed.
 * @constructor
 * @param {String} myName The human readable link name, used for innerHTML of
 * the anchor when outputting to the user.
 * @param {String} myUrl The actual href URL for the anchor
 * @return A new keywordUrlMapClass
 */
mi.Search.prototype.keywordUrlMapClass = function( myName, myUrl){
    this.name		= myName;		// Name of link to display
    this.url		= myUrl;		// Actual URL
    this.keywords	= [];			// An array of keywords match
    this.matchedKeyword = false;		// This is set to true when we
						// match a keyword to prevent dups
        
    
     /**
    * Adds a new keyword to the Array 'keywords' for the current instance of
    * this object, also lowercases the keyword
    * @type String
    */
    this.addKeyword 	= function( myKeyword ){
	this.keywords.push( myKeyword.toLowerCase() );
    }
    
    /**
     * Given a passed keyword, see if it matches any keywords in this object,
     * if so then return true, and set that object as matchedKeyword == true
     * to prevent duplicate outputs
     * @type String
     * @return 'true' if match found, 'false' otherwise
    */
    this.matchKeyword 	= function( myKeyword ){
	
	if( ( !this.matchedKeyword ) && ( this.getKeywords().match( myKeyword ) ) ){
		this.matchedKeyword 	= true;
		return( true );
	}
	return( false );
    }
    
    /**
     * Will return a list of this objects instance keywords, in comma delimited
     * format.
     * @return String of comma delimited keywords
     */
    this.getKeywords 	= function( ){
	
	return( this.keywords.join( ', ' ) );
    }
    
}
// *****************************************************************************


// *****************************************************************************
// Function:	fetchSearchResults( 'myTargetSelector' )
// Arguments:	myTargetSelector: JQuery style selector to inject SOLR results in
// Purpose:	Based on keywords extrapolated from the current URL, will inject
// 		SOLR search results into the passed JQuery selector
// Return:	NA
// *****************************************************************************
mi.Search.prototype.fetchSearchResults = function( myTargetSelector ){
    
    // 'keywordList' is a space separated list of keywords found in the URL
    var keywordList 	= '';
    
    // Get the URL and send to function to get keywords, will return an array
    //   of keywords.
    var keywords 		= this.fetch404Keywords( );
    
    // Here we loop through the keywords, and assemble into a space separated
    //   string that SOLR can parse
    for( var i in keywords ){
	keywordList += ' ' + keywords[i];
    }
    
    // Now inject the search results into the passed selector
    $( myTargetSelector ).load( '/search/ #search', { q: keywordList } );

}
// *****************************************************************************


// *****************************************************************************
// Function:	fetch404Keywords( )
// Purpose:	Will parse for all words between forward slashes after the
// 		domain name and return this list of words as an array
// Return:	An array of keywords found in the url after the domain name
// *****************************************************************************
mi.Search.prototype.fetch404Keywords = function( ){
    
    // This will be the array that holds the unedited version of all 404 keywords
    var keywordsArray 		= [];
    // This will be the array returned by this function containing all keywords
    //   after filtering out the 'bad' keywords as defined by the regex below
    var returnKeywordsList 	= [];
    
    // Get the list of 404 keywords from the current URL
    keywordsArray = window.location.pathname.toLowerCase().slice(1).split('/');
    
    // Go through all the keywords and filter out for 'invalid' keywords
    //   based on the regex in the loop.
    for( var x in keywordsArray ){	

	// If the current keyword doesn't match the regex then assign on the
	//   returned keyword array
	if(  ( keywordsArray[x].match( /story/ ) ) || ( keywordsArray[x].match( /[0-9]+/ ) ) ){
	    //console.log( 'INVALID KEYWORD FOUND: ' + keywordsArray[x] );
	} else {
	    //console.log( 'VALID KEYWORD FOUND: ' + keywordsArray[x] ); 
	    returnKeywordsList.push( keywordsArray[x] );
	}
    }
	
    return( returnKeywordsList );

}

/**
 * @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://theolathenews.com/search-bin/search.pl.cgi");
                                this.setConf("query_fields",{sf_Keywords:search_text,
                                                                product:"Yahoo,Overture",
                                                                collection:"WEB",
                                                                live_template:"http://www.theolathenews.com/searchresults/v-ysr/index.html",
                                                                error_template:"http://www.theolathenews.com/searchresults/v-yerr/index.html",
                                                                preview_template:"http://preview.theolathenews.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:"ODNB",
                                                                dbquery:search_text,
                                                                collection:"ARCHIVES"}
                                                );
                        break;
                        default:
                                this.configErrorReporter();
                }
        }
};

/*
This javascript is an object oriented version of MI's Story Tools.  Much of the
code here was developed by Lucas Myers.  It has since been slightly modified to
fit into the Reference Site, and some additions have been made.  Gabe Doliner
made it object oriented.

This script is called from and used by the story detail template only
*/

/* object constructor */
function miStoryTool() {
    /* story tool functions */
    this.toolstate = "off";
    this.toolnames = new Array();
    this.toolnames['email'] = "email this story";

    //initialize the story tool
    this.storyTool = function( tool, url ) {
        // set title of tool
        $("#toolname").empty();
        $("#toolname").append(this.toolnames[tool]);
        // clean up tool area
        $("#tool").empty();
        $("#tool").append("loading...");

        // send request for tool, display if loads ok otherwise display error
        $.ajax({
            type: "GET",
            url: url,

            success: function(msg) {
                $("#tool").empty();
                $("#tool").append(msg);
            },
            error: function() {
                $("#tool").empty();
                $("#tool").append("There was a problem loading this tool.");
            }
        });

        // display the toolbox
        if ( this.toolstate == "off" ) {
            $("#toolbox").fadeIn("fast");
            this.toolstate = "on";
        }
    }

    //close the tool
    this.closeTool = function() {
        // hide toolbox
        $("#toolbox").fadeOut("fast");
        this.toolstate = "off";
        //$("#toolbox").css("top","0px");
    }

    
    this.sendStory = function(theForm) {
        // validate form
        if ( this.validate(theForm) === true )
        {
            // clear tool and display message
            $("#tool").empty();
            $("#tool").append("sending...");
		
	// Initialize recaptcha variables
	var recaptcha_response = typeof(theForm.recaptcha_response_field) == 'undefined' ? '' : theForm.recaptcha_response_field.value;
	var recaptcha_challenge = typeof(theForm.recaptcha_challenge_field) == 'undefined' ? '' : theForm.recaptcha_challenge_field.value;
        
	    // send form for processing
            $.ajax({
                type: "POST",
                url: theForm.action,
                data: { domain: theForm.domain.value, url_form: theForm.url_form.value, email_type: theForm.email_type.value, url_html: theForm.url_html.value, url_story: theForm.url_story.value, to_email: theForm.to_email.value, from_email: theForm.from_email.value, from_name: theForm.from_name.value, comments: theForm.comments.value, headline: theForm.headline.value, recaptcha_challenge_field: recaptcha_challenge, recaptcha_response_field: recaptcha_response },
                success: function(msg){
                    // clear tool message, display message from server
                    $("#tool").empty();
                    $("#tool").append(msg);
                    //$("#emailForm").empty();
                    
                    // close tool after delay
                    //$("#toolbox").fadeOut(3000);
                    //this.toolstate = "off";
                },
                error: function(){
                    $("#tool").empty();
                    $("#tool").append("There was a problem sending this story, please try again.");
                }
            });
        }

        return false;
    }

    this.mvForm = function() {
        this.adj = $("#story_body").height() - 150;
        $("#toolbox").css( "top", this.adj );
    }

    this.validate = function (theForm) {
        this.theForm = theForm;
        with(this.theForm)
        {
            // CHECK NAME
            if (from_name.value == "") {
                alert("Please enter your name.");
                from_name.focus();
                return false;
            }

            // Check "To" email address(es)
            if ( to_email.value == "" ) {
                alert( "Please enter a 'To' email address!" );
                to_email.focus();
                return false;
            }
            this.emailArr = to_email.value.split(',');
            if ( this.emailArr.length > 5 ) {
                alert( "Only 5 'To' email addresses are allowed!" );
                to_email.focus();
                return false;
            }
            for (var i = 0; i < this.emailArr.length; i++) {
                if ( !this.validateEmail( this.emailArr[i] ) ) {
                    alert( "'To' email address [" + this.emailArr[i] + "] is invalid" );
                    to_email.focus();
                    return false;
                }
            }

            // Check "From" email address
            if ( from_email.value == "" ) {
                alert("Please enter a 'From' email address!");
                from_email.focus();
                return false;
            }
            if ( !this.validateEmail ( from_email.value ) ) {
                alert("Please enter a valid 'From' email address!");
                from_email.focus();
                return false;
            }

            return(true);
        }  //  with(theForm)
    }  //  END  validate()

    this.trim = function(str) {
        this.str = str;
        return this.str.replace(/^\s+|\s+$/g, '');
    }

    this.validateEmail = function(valfield) {
        this.tfld = this.trim( valfield );  // value of field with whitespace trimmed off
        this.email = /^[^@]+@[^@.]+\.[^@]*\w\w$/;
        return ( !this.email.test( this.tfld ) ) ? false : true;
    }
}
/* end object constructor */

/* instantiate the object */
mi_story_tool = new miStoryTool();
/* emailastory.js *************************************************************
 * loads the mail-a-friend tool into the storyTool box
 * @minify true
 */
var rscriptloaded = 0;
function displayEmailForm(url) {
        if(rscriptloaded) {
                mi_story_tool.storyTool("email", url);
        } else {
                $.getScript("http://api.recaptcha.net/js/recaptcha_ajax.js", function() {
                        mi_story_tool.storyTool("email", url);
                        rscriptloaded = 1;
                });
        }
}
/* ^ emailastory.js ******************************************************** */

