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

/*
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
        jQuery("#toolname").empty();
        jQuery("#toolname").append(this.toolnames[tool]);
        // clean up tool area
        jQuery("#tool").empty();
        jQuery("#tool").append("loading...");

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

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

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

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

    
    this.sendStory = function(theForm) {
        // validate form
        if ( this.validate(theForm) === true )
        {
            // clear tool and display message
            jQuery("#tool").empty();
            jQuery("#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
            jQuery.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
                    jQuery("#tool").empty();
                    jQuery("#tool").append(msg);
                    //jQuery("#emailForm").empty();
                    
                    // close tool after delay
                    //jQuery("#toolbox").fadeOut(3000);
                    //this.toolstate = "off";
                },
                error: function(){
                    jQuery("#tool").empty();
                    jQuery("#tool").append("There was a problem sending this story, please try again.");
                }
            });
        }

        return false;
    }

    this.mvForm = function() {
        this.adj = jQuery("#story_body").height() - 150;
        jQuery("#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();
// $Id: MI.js 1603 2011-03-24 20:29:00Z bjones $
/** MI.js **********************************************************************
 * @fileoverview Inclusion of this library creates an MI object within a
 * variable named <tt>mi</tt>. The MI object is intended to namespace
 * functionality developed by McClatchy Interactive. Because the MI object is
 * self-instantiating there is no constructor per se, and there should be no
 * reason to try to create any additional copies/instances, it is a generic
 * object.
 *
 * <p><strong>window.console</strong><br>In addition to automatically creating
 * the <tt>mi</tt> variable this library also will create a
 * <tt>window.console</tt> object in browsers that don't already have one. The
 * window.console object is then provided with Firebug-like methods if they do
 * not already exist. At the very least this allows you to use
 * <tt>console.log()</tt> in your code without the fear of throwing errors in
 * browsers that don't support it. <em>Note:</em> Safari natively supports
 * console.log, but not (m)any of the other Firebug console methods. With Safari
 * and Firefox/Firebug output from console methods can be viewed in the
 * browser's console window. In browsers without a console window the console's
 * log can be accessed via an alert window at page load if the page is loaded
 * with the query string <em>?viewlog=1</em>. Currently all supported Firebug
 * methods are basically aliases for the log, though the log will report what
 * method was used. Future updates may provide additional features with these
 * methods to make them more Firebug-like.</p>
 *
 * <p><strong>Extending the MI object</strong><br>
 * The MI object is an evolving piece of code and extending its functionality is
 * encouraged. However, we do ask that you follow these guidelines.</p>
 * <ul>
 * 	<li><strong>Document your code -</strong> This was specifically chosen as 
 * 		the first item. The more the better. With the aggregator's ability to strip 
 * 		comments don't be shy about putting too much documentation into your code. 
 * 		For the win, write your documentation in the format used by JSDoc so that 
 * 		your documentation can be parsed. See this file as an example. Inclusion of 
 * 		the <tt>&#64;minify true</tt> flag will allow your code to be minified, thus 
 * 		stripping whitespace and comments from code used on live sites.</li>
 * 	<li><strong>More, smaller files -</strong> Your code should be included on
 * 		pages via aggregation, so there's no need to write huge library files that
 * 		contain the kitchen sink. Organize your code into files of related 
 * 		functionality.</li>
 * 	<li><strong>Consolidate features into apps -</strong> The MI object provides
 * 		the <tt>mi.App</tt> class that facilitates easy creation of applications
 * 		for specific features.</li>
 * 	<li><strong>Stick to naming conventions -</strong>
 * 		<ul>
 * 			<li>CamelCase constructors with an initial cap</li>
 * 			<li>Start private method and variable names with an underscore</li>
 * 			<li>File names should imply scope, separated by underscores. For example
 * 			<tt>MI_Search.js</tt>.</li>
 * 		</ul>
 * 	</li>
 * 	<li><strong>Overwrite similar functionality -</strong> There's no need to 
 * 		have multiple apps or methods that do similar tasks. Instead overwrite the
 * 		existing feature with your new version. Even better, make your new version
 * 		accept input and produce output the same as the old version, but with 
 * 		additional options to facilitate your new behavior. This will preserve 
 * 		backward compatability.</li>
 * 	<li><strong>Contain dependencies -</strong> Don't rely on, or trust in 
 * 		global variables. Either pass values in during execution, or make them
 * 		configurable options.</li>
 * 	<li><strong>Generalize -</strong> Any code within the MI object should be 
 * 		considered general in that it could be utilized on any site. No 
 * 		site-specific code. Any site-specific values should be configuration 
 * 		options.</li>
 * 	<li><strong>Exit gracefully -</strong> With the console now available on all
 * 		browsers there's little or no need to use <tt>alert()</tt> and fear users
 * 		getting messages meant for developers. Test for dependencies and exit if
 * 		they don't meet your requirements after outputting a message to the 
 * 		console.</li>
 * </ul>
 * 
 * @minify true
 * @author Joe Whetzel (jwhetzel [at] mcclatchyinteractive.com)
 * @namespace mi
 * @aggpath js/MI.js
 */

var mi = (typeof mi == 'undefined') ? {'media_domain':''} : mi;
if (window.miAppControler) {
	mi.control = new miAppControler();
}



/** 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.
 * @author 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.
 * @private
 */
mi._console = function(s) {
	mi._console.log = (mi._console.log && mi._console.log.length > 0) ? mi._console.log + '\n---------------------------------------------------\n' + s : s;
};



/** Use console methods even in browsers without a console.
 * Defines a console object in browsers that lack one and then populates the
 * console with any missing methods from a list based on those used by Firebug.
 * Any methods created in this manner act effectively as a self-identifying
 * alias for console.log.
 *
 * <p>This method is automatically executed as the code is loaded. With this
 * in place developers can make use of console methods without worrying about
 * causing errors on browsers with no console. This makes troubleshooting during
 * development easier, as well as allowing standing error reporting features to
 * be utilized even on live pages.</p>
 *
 * This method is based on Pluck's NYX object method of the same name.
 */
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);});
							}
						} 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 method/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
 * @param {Object} sourceObj The object to be cloned.
 * @return A copy of the source object, <b>not</b> a reference to the original
 * @type Object
 */
mi.cloneObject = function(sourceObj) {
	if (sourceObj == null || typeof sourceObj != 'object') {
		return sourceObj;
	}
	var temp = new sourceObj.constructor();
	for (var key in sourceObj) {
			temp[key] = mi.cloneObject(sourceObj[key]);
	}
	return temp;
};


/** A constructor for applications that come pre-loaded with useful features.
 *
 * <p>Application objects come with features that facilitate the management of
 * configuration values with a system of methods used to make setting and 
 * accessing configuration values easily while protecting those settings from 
 * accidental or malevolent corruption.</p>
 * <h2>Creating your App</h2>
 * <p>In order to properly inherit all of the private properties that keep your 
 * configuration settings safe you need to use a somewhat non-traditional manner 
 * to instantiate your App. Instead of creating an instance of mi.App you 
 * instead create a constuctor for your App and inherit from the mi.App "class",
 * making your constructor a sub-class of mi.App.</p>
 * <h4>Usage Example:</h4>
 * <pre>mi.YourApp &#61; function() {
  mi.App.apply(this, arguments);
}</pre>
 * <h2>Enforcing config values</h2>
 * <p>With the only way to set configuration values being via the 
 * {@link #setConf} method you have the ability to define rules around what 
 * kinds of values are acceptable per configurable option. This is accomplished 
 * by defining a method named <tt>_manageConf</tt> specifically to
 * apply your rules. It is up to you to develop the enforcement of your rules. 
 * Here's an example:</p>
 * <h4>Usage Example:</h4>
 * <pre>mi.YourApp &#61; function() {
  mi.App.apply(this, arguments);
  this._manageConf = function(prop, val) {  
    switch (prop) {
      case 'gender':          // each case is based on the name of the configurable option
        if (
          val != 'male' ||
          val != 'female' ||
          val != 'unknown'
        ) {
          val = 'unknown';
        }
      break;
    }
    return val;
  };
}</pre>
 * <p>This example only enforces the setting of the <i>gender</i> configuration,
 * with three possible values. If an unacceptable value is passed the config 
 * gets set to an acceptable default value. Set up a case for each configuration
 * that needs enforcement. In any case, your method needs to accept two variables,
 * the name of the config and the value, and must return the value to be used.</p>
 * @constructor
 */
mi.App = function() {
	var _configs = {};
	/**
	 * Stand in method to be used for managing configuration values. By default
	 * this method does not do anything. Individual apps have the option to overwrite
	 * this method with their own functionality.
	 * @private
	 */
	this._manageConf = function(prop, val) { return val; };
	/**
	 * Set configuration values in the app.
	 * <p>Configurations may be loaded in one of two ways:</p>
	 * <ol><li>Individually: Pass two arguments, the first being the name of the
	 * configuration value and the second being the value, or</li>
	 * <li>Batch: Pass an object with attributes named after the config name and
	 * their values being the desired config setting.</li></ol>
	 * <p>Actually, you can also use these two means of configuring your app when
	 * you instantiate it by passing arguments to the constructor.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.yourApp1 &#61; new mi.YourApp('gender','male');
mi.yourApp2 &#61; new mi.YourApp({'gender':'unknown','name':'Pat'});</pre>
	 * <p>If you have multiple configurations to set at one time, passing an object
	 * is probably the most efficient means of getting them set.</p>
	 * @param {Object} confs A generic object containing one or more attributes
	 * that will be used to create the configuration(s), or
	 * @param {String} name The name of the configuration value to be set, this
	 * should be a string value, and
	 * @param value The value to be used.
	 */
	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;
			default:
				console.warn('mi.App.setConf was passed an incorrect number of arguments, the method should be used with either a name-value pair or an object containing configuration settings.');
		}
	};
	/**
	 * Retreive a configuration value from the app.
	 *
	 * <p>Any configuration value can be retrieved using this method. Simply pass
	 * the name of the config setting as the one argument. The value of the 
	 * setting is returned.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.example.setConf('name','Fred');
name &#61; mi.example.getConf('name');	//name is now equal to "Fred"</pre>
	 * @param {String} prop The configuration setting name you want returned; a string.
	 * @return The value associated with the named setting.
	 */
	this.getConf = function(prop) {
		return _configs[prop];
	};
	/**
	 * Outputs all configuration settings to the console.
	 *
	 * A convenience method for troubleshooting. Calling this method will output
	 * the name and value of each configuration setting in the app.
	 */
	this.viewConfs = function() {
		console.dir(_configs);
	};
	/**
	 * Object used for storing temporary values.
	 *
	 * <p>Rather than littering your app with variables used by the app's methods
	 * this object is provided as a bucket for storing those values. There are no
	 * controls around what can be set in this object. Basically it's an 
	 * unprotected bucket, so values shouldn't necessarily be trusted, test them
	 * before relying on them.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.exampleApp.cache.foo = "bar";</pre>
	 */
	this.cache = {};
	/* pass any arguments on to setConf() to configure the app as it's instantiated
	 */
	switch (arguments.length) {
		case 1:
			this.setConf(arguments[0]);
			break;
		case 2:
			this.setConf(arguments[0], arguments[1]);
			break;
	}
};


/** A Method for discovering the object/node that kicked off the current event.
 *
 * <p>This can be a very handy method to make determining what element kicked
 * off an even a snap. It can also be frustrating without proper documentation.
 * Basically, when called correctly this will return the source element of the
 * current event.</p>
 * <b>Usage Example:</b><br>
 * <pre>jQuery(window).click(function(e){
	console.log(mi.getEventSrc(e));
});</pre>
 * <p>In this case the object that was clicked on will be output to the console.
 * Due to event bubbling it is the object clicked, not the object with the
 * listener that is reported. That's what makes this so useful. So if in this
 * example you clicked on a paragraph object it would be that paragraph that
 * would be returned not the window object.</p>
 * <p><i>Note:</i> it is key that an argument representing the event object is 
 * passed to the handler for browsers that do not support IE's <tt>window.event</tt>
 * object.</p>
 */
mi.getEventSrc = function (e) {
	if (!e) {e = window.event;}
	if (e.target) {
		return e.target;
	} else if (e.srcElement) {
		return e.srcElement;
	}
};


/**
 * Pattern used by {@link #templateParser} to find variables.
 * @type RegEx
 */
mi.templateVarPattern = /\@([^\@]+)\@/g;
/** method for parsing a template and replacing a pattern with the equivalent
 * attributes from an object
 *
 * <pre>var data object to get values from
 *var template string containing placeholders</pre>
 *
 * <p>Placeholders in the template should be given the name of the attribute to be 
 * used as the substitute surrounded by "@" symbols, i.e. @name@</p>
 *
 * <p>The pattern is defined outside of the method to avoid instantiating the 
 * pattern every time the method is used.</p>
 *
 * @param {Object} data Attributes should be the name of the variable to be searched for
 * and value is what will be put into the template.
 * @param {String} template The template string used to format the output.
 * @return The template with each variable replaced with the corresponding value from
 * the <i>data</i> argument.
 * @type String
 */
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
 *
 * @param {Object} sourceData Each attribute will be made into part of the resulting string.
 * @param {String} firstDelimiter Delimiter to be used between attributes.
 * @param {String} secondDelimiter Delimiter to be used between the name and the value.
 * @author Jamie Kirk
 * @type String
 */
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.');
	}
};

/**
 * Checks for a pageInfo object in the global namespace and loads any data, that
 * doesn't already exist, into the mi.pageInfo object.
 * <p>Any objects will be cloned, not referenced, and pre-existing values will 
 * not be overwritten.</p>
 * <p> This method only officially supports one nested object, 
 * i.e. pageInfo.asset.id. A second-level nested object may be created,
 * i.e., pageInfo.asset.foo.bar; however, pageInfo.asset.foo cannot then
 * accept additional attributes, nor can pageInfo.asset.foo.bar be overwritten.</p>
 * <p>The global object is nullified after loading is complete to encourage
 * accessing data in the mi object.</p>
 */
mi.loadPageInfo = function() {
	if (window.pageInfo) {
		var pi = window.pageInfo;
		if (this.pageInfo == undefined) {
			this.pageInfo = this.cloneObject(pi);
		} else {
			for (var key in pi) {
				if (key === 'version' && ( parseFloat(pi[key]) > parseFloat(this.pageInfo.version) ) ) {
					this.pageInfo.version = pi[key];
				} else if (this.pageInfo[key] == undefined) {
					this.pageInfo[key] = this.cloneObject(pi[key]);
				} else if (typeof this.pageInfo[key] == 'object') {
					for (var key2 in pi[key]) {
						this.pageInfo[key][key2] = (this.pageInfo[key][key2]) ? this.pageInfo[key][key2] : this.cloneObject(pi[key][key2]);
					}
				}
			}
		}
	}
	window.pageInfo = null;
}

/** method for ensuring that js executes only after the document is ready
 *
 * @param {Integer} time How long (in seconds) to wait for the document to render
 * @param {String} target A JQuery-type selector
 * @param {Object} callback The function to execute when the document is ready
 * @author Scot Billman
 */
mi.wait_for_ready = function( time, target, callback ){
   var checker, time_spent = 0, interval = 3000;

   _check_document = function(){
      if( null !== $(target) ){
         clearInterval( checker );
         callback();
      } else {
         time_spent += interval/1000;
         if( time_spent >= time ){
            clearInterval( checker );
         }
      }
   };

   $(document).ready( function() {
      checker = setInterval( _check_document, interval );
   });
};

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

/** DealSaver.js **********************************************************************
 * @minify true
 * @author Todd Edwards (tedwards [at] mcclatchyinteractive.com)
 * @aggpath dealsaver/js/MI_DealSaver.js
 */

mi.DealSaver = function() {
  mi.App.apply(this, arguments);
  if (mi.control && mi.control.dealsaver !== undefined) {
    this.setConf("enabled",mi.control.dealsaver);
  } else {
    this.setConf("enabled",0);
    console.warn("DealSaver has been instantiated, but disabled because mi.control.dealsaver is not defined.");
  }
};

mi.DealSaver.prototype.executeDs = function() {
  var self = this;
  var e = self.getConf("enabled");

  if ( e !== 0 ) {
    dsUrl = "http://" + window.location.hostname + "/static/dealsaver/widget/dealsaver.json";

    jQuery.ajax({
      type: "GET",
      cache: false,
      dataType: "json",
      url: dsUrl,
      success: function(data) {
        self.checkData(data);
        self.displayWidget(self.getConf("enabled"));
      },
      error: function() {
        self.setConf("enabled",0);
        self.displayWidget(self.getConf("enabled"));
      }
    });
  }
  else {
    console.info('Display of DealSaver has been disabled.');
  }
};

mi.DealSaver.prototype.checkData = function(data) {
  var self = this;

  if (data.page.deals.deal == undefined) {
    self.setConf("enabled",0);
    console.warn("The DealSaver widget has been disabled because it can't find any deal information in the feed.");
  } else if (data.page.deals.deal.saleprice.$t <= 0 || data.page.deals.deal.saleprice.$t == undefined) {
    self.setConf("enabled",2);
    console.warn("The DealSaver widget has been placed in PlaceHolder mode because saleprice is empty or 0.");
    self.distributeData(data);
  } else {
    self.distributeData(data);
  }
}

mi.DealSaver.prototype.distributeData = function(data) {
  var self = this;

  if (self.getConf("LID") !== undefined) {
    var lid = self.getConf("LID");
    var lidHash = "&LID=" + lid;
  } else {
    console.warn("DealSaver can't find mi.dealSaver.getConf('LID'). Disabling the LID hash tag in URLs.");
    var lidHash = '';
  }

  var dsvalue = data.page.deals.deal.productvalue.$t;
  var dsprice = data.page.deals.deal.saleprice.$t;
  var dollarsoff = (dsvalue-dsprice);
  var percentoff = (dollarsoff / dsvalue) * 100;
  var misitelink = data.page.site.sitelink.$t + lidHash + "#widget=ds_rrail";
  var mideallink = data.page.deals.deal.link.$t + lidHash + "#widget=ds_rrail";

  jQuery("#ds_value").html("$"+Math.round(dsvalue));
  jQuery("#ds_discount").html(Math.floor(percentoff)+"%");
  jQuery("#ds_save").html("$"+Math.round(dollarsoff));
  jQuery("#dealsaver_td .ds_title_link").html(data.page.deals.deal.offer.$t);
  jQuery("#dealsaver_td .ds_pricetag_container").html("$"+data.page.deals.deal.saleprice.$t);
  jQuery("#dealsaver_td .ds_deal_image img").attr("src",data.page.deals.deal.splashpagethumbnail.$t);
  jQuery("#dealsaver_td .ds_logo_link").attr("href",misitelink);
  jQuery("#dealsaver_td .ds_dealtitle").attr("href",misitelink);
  jQuery("#dealsaver_td .ds_title_link").attr("href",mideallink);
  jQuery("#dealsaver_td .ds_deal_image a").attr("href",mideallink);
  jQuery("#dealsaver_td .ds_pricetag a").attr("href",mideallink);
};

mi.DealSaver.prototype.displayWidget = function(display_mode) {
  var self = this;

  if ( display_mode !== 0 && display_mode !== 2 ) {
    jQuery("#dealSaverWidget").attr("style", "display:block");
  } else if ( display_mode == 2 ) {
    jQuery("#dealSaverWidget .ds_buycontainer").attr("style", "display:none");
    jQuery("#dealSaverWidget .ds_deal_image").attr("style", "float:none; text-align:center");
    jQuery("#dealSaverWidget").attr("style", "display:block");
  }
};
/** MI.js **********************************************************************
 * @fileoverview Inclusion of this library creates an MI object within a
 * variable named <tt>mi</tt>. The MI object is intended to namespace
 * functionality developed by McClatchy Interactive. Because the MI object is
 * self-instantiating there is no constructor per se, and there should be no
 * reason to try to create any additional copies/instances, it is a generic
 * object.
 *
 * <p><strong>window.console</strong><br>In addition to automatically creating
 * the <tt>mi</tt> variable this library also will create a
 * <tt>window.console</tt> object in browsers that don't already have one. The
 * window.console object is then provided with Firebug-like methods if they do
 * not already exist. At the very least this allows you to use
 * <tt>console.log()</tt> in your code without the fear of throwing errors in
 * browsers that don't support it. <em>Note:</em> Safari natively supports
 * console.log, but not (m)any of the other Firebug console methods. With Safari
 * and Firefox/Firebug output from console methods can be viewed in the
 * browser's console window. In browsers without a console window the console's
 * log can be accessed via an alert window at page load if the page is loaded
 * with the query string <em>?viewlog=1</em>. Currently all supported Firebug
 * methods are basically aliases for the log, though the log will report what
 * method was used. Future updates may provide additional features with these
 * methods to make them more Firebug-like.</p>
 *
 * <p><strong>Extending the MI object</strong><br>
 * The MI object is an evolving piece of code and extending its functionality is
 * encouraged. However, we do ask that you follow these guidelines.</p>
 * <ul>
 * 	<li><strong>Document your code -</strong> This was specifically chosen as 
 * 		the first item. The more the better. With the aggregator's ability to strip 
 * 		comments don't be shy about putting too much documentation into your code. 
 * 		For the win, write your documentation in the format used by JSDoc so that 
 * 		your documentation can be parsed. See this file as an example. Inclusion of 
 * 		the <tt>&#64;minify true</tt> flag will allow your code to be minified, thus 
 * 		stripping whitespace and comments from code used on live sites.</li>
 * 	<li><strong>More, smaller files -</strong> Your code should be included on
 * 		pages via aggregation, so there's no need to write huge library files that
 * 		contain the kitchen sink. Organize your code into files of related 
 * 		functionality.</li>
 * 	<li><strong>Consolidate features into apps -</strong> The MI object provides
 * 		the <tt>mi.App</tt> class that facilitates easy creation of applications
 * 		for specific features.</li>
 * 	<li><strong>Stick to naming conventions -</strong>
 * 		<ul>
 * 			<li>CamelCase constructors with an initial cap</li>
 * 			<li>Start private method and variable names with an underscore</li>
 * 			<li>File names should imply scope, separated by underscores. For example
 * 			<tt>MI_Search.js</tt>.</li>
 * 		</ul>
 * 	</li>
 * 	<li><strong>Overwrite similar functionality -</strong> There's no need to 
 * 		have multiple apps or methods that do similar tasks. Instead overwrite the
 * 		existing feature with your new version. Even better, make your new version
 * 		accept input and produce output the same as the old version, but with 
 * 		additional options to facilitate your new behavior. This will preserve 
 * 		backward compatability.</li>
 * 	<li><strong>Contain dependencies -</strong> Don't rely on, or trust in 
 * 		global variables. Either pass values in during execution, or make them
 * 		configurable options.</li>
 * 	<li><strong>Generalize -</strong> Any code within the MI object should be 
 * 		considered general in that it could be utilized on any site. No 
 * 		site-specific code. Any site-specific values should be configuration 
 * 		options.</li>
 * 	<li><strong>Exit gracefully -</strong> With the console now available on all
 * 		browsers there's little or no need to use <tt>alert()</tt> and fear users
 * 		getting messages meant for developers. Test for dependencies and exit if
 * 		they don't meet your requirements after outputting a message to the 
 * 		console.</li>
 * </ul>
 * 
 * @minify true
 * @author Joe Whetzel (jwhetzel [at] mcclatchyinteractive.com)
 * @namespace mi
 * @aggpath js/MI.js
 */

var mi = (typeof mi == 'undefined') ? {'media_domain':''} : mi;
if (window.miAppControler) {
	mi.control = new miAppControler();
}



/** 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.
 * @author 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.
 * @private
 */
mi._console = function(s) {
	mi._console.log = (mi._console.log && mi._console.log.length > 0) ? mi._console.log + '\n---------------------------------------------------\n' + s : s;
};



/** Use console methods even in browsers without a console.
 * Defines a console object in browsers that lack one and then populates the
 * console with any missing methods from a list based on those used by Firebug.
 * Any methods created in this manner act effectively as a self-identifying
 * alias for console.log.
 *
 * <p>This method is automatically executed as the code is loaded. With this
 * in place developers can make use of console methods without worrying about
 * causing errors on browsers with no console. This makes troubleshooting during
 * development easier, as well as allowing standing error reporting features to
 * be utilized even on live pages.</p>
 *
 * This method is based on Pluck's NYX object method of the same name.
 */
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);});
							}
						} 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 method/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
 * @param {Object} sourceObj The object to be cloned.
 * @return A copy of the source object, <b>not</b> a reference to the original
 * @type Object
 */
mi.cloneObject = function(sourceObj) {
	if (sourceObj == null || typeof sourceObj != 'object') {
		return sourceObj;
	}
	var temp = new sourceObj.constructor();
	for (var key in sourceObj) {
			temp[key] = mi.cloneObject(sourceObj[key]);
	}
	return temp;
};


/** A constructor for applications that come pre-loaded with useful features.
 *
 * <p>Application objects come with features that facilitate the management of
 * configuration values with a system of methods used to make setting and 
 * accessing configuration values easily while protecting those settings from 
 * accidental or malevolent corruption.</p>
 * <h2>Creating your App</h2>
 * <p>In order to properly inherit all of the private properties that keep your 
 * configuration settings safe you need to use a somewhat non-traditional manner 
 * to instantiate your App. Instead of creating an instance of mi.App you 
 * instead create a constuctor for your App and inherit from the mi.App "class",
 * making your constructor a sub-class of mi.App.</p>
 * <h4>Usage Example:</h4>
 * <pre>mi.YourApp &#61; function() {
  mi.App.apply(this, arguments);
}</pre>
 * <h2>Enforcing config values</h2>
 * <p>With the only way to set configuration values being via the 
 * {@link #setConf} method you have the ability to define rules around what 
 * kinds of values are acceptable per configurable option. This is accomplished 
 * by defining a method named <tt>_manageConf</tt> specifically to
 * apply your rules. It is up to you to develop the enforcement of your rules. 
 * Here's an example:</p>
 * <h4>Usage Example:</h4>
 * <pre>mi.YourApp &#61; function() {
  mi.App.apply(this, arguments);
  this._manageConf = function(prop, val) {  
    switch (prop) {
      case 'gender':          // each case is based on the name of the configurable option
        if (
          val != 'male' ||
          val != 'female' ||
          val != 'unknown'
        ) {
          val = 'unknown';
        }
      break;
    }
    return val;
  };
}</pre>
 * <p>This example only enforces the setting of the <i>gender</i> configuration,
 * with three possible values. If an unacceptable value is passed the config 
 * gets set to an acceptable default value. Set up a case for each configuration
 * that needs enforcement. In any case, your method needs to accept two variables,
 * the name of the config and the value, and must return the value to be used.</p>
 * @constructor
 */
mi.App = function() {
	var _configs = {};
	/**
	 * Stand in method to be used for managing configuration values. By default
	 * this method does not do anything. Individual apps have the option to overwrite
	 * this method with their own functionality.
	 * @private
	 */
	this._manageConf = function(prop, val) { return val; };
	/**
	 * Set configuration values in the app.
	 * <p>Configurations may be loaded in one of two ways:</p>
	 * <ol><li>Individually: Pass two arguments, the first being the name of the
	 * configuration value and the second being the value, or</li>
	 * <li>Batch: Pass an object with attributes named after the config name and
	 * their values being the desired config setting.</li></ol>
	 * <p>Actually, you can also use these two means of configuring your app when
	 * you instantiate it by passing arguments to the constructor.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.yourApp1 &#61; new mi.YourApp('gender','male');
mi.yourApp2 &#61; new mi.YourApp({'gender':'unknown','name':'Pat'});</pre>
	 * <p>If you have multiple configurations to set at one time, passing an object
	 * is probably the most efficient means of getting them set.</p>
	 * @param {Object} confs A generic object containing one or more attributes
	 * that will be used to create the configuration(s), or
	 * @param {String} name The name of the configuration value to be set, this
	 * should be a string value, and
	 * @param value The value to be used.
	 */
	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;
			default:
				console.warn('mi.App.setConf was passed an incorrect number of arguments, the method should be used with either a name-value pair or an object containing configuration settings.');
		}
	};
	/**
	 * Retreive a configuration value from the app.
	 *
	 * <p>Any configuration value can be retrieved using this method. Simply pass
	 * the name of the config setting as the one argument. The value of the 
	 * setting is returned.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.example.setConf('name','Fred');
name &#61; mi.example.getConf('name');	//name is now equal to "Fred"</pre>
	 * @param {String} prop The configuration setting name you want returned; a string.
	 * @return The value associated with the named setting.
	 */
	this.getConf = function(prop) {
		return _configs[prop];
	};
	/**
	 * Outputs all configuration settings to the console.
	 *
	 * A convenience method for troubleshooting. Calling this method will output
	 * the name and value of each configuration setting in the app.
	 */
	this.viewConfs = function() {
		console.dir(_configs);
	};
	/**
	 * Object used for storing temporary values.
	 *
	 * <p>Rather than littering your app with variables used by the app's methods
	 * this object is provided as a bucket for storing those values. There are no
	 * controls around what can be set in this object. Basically it's an 
	 * unprotected bucket, so values shouldn't necessarily be trusted, test them
	 * before relying on them.</p>
	 * <b>Usage Example:</b><br>
	 * <pre>mi.exampleApp.cache.foo = "bar";</pre>
	 */
	this.cache = {};
	/* pass any arguments on to setConf() to configure the app as it's instantiated
	 */
	switch (arguments.length) {
		case 1:
			this.setConf(arguments[0]);
			break;
		case 2:
			this.setConf(arguments[0], arguments[1]);
			break;
	}
};


/** A Method for discovering the object/node that kicked off the current event.
 *
 * <p>This can be a very handy method to make determining what element kicked
 * off an even a snap. It can also be frustrating without proper documentation.
 * Basically, when called correctly this will return the source element of the
 * current event.</p>
 * <b>Usage Example:</b><br>
 * <pre>jQuery(window).click(function(e){
	console.log(mi.getEventSrc(e));
});</pre>
 * <p>In this case the object that was clicked on will be output to the console.
 * Due to event bubbling it is the object clicked, not the object with the
 * listener that is reported. That's what makes this so useful. So if in this
 * example you clicked on a paragraph object it would be that paragraph that
 * would be returned not the window object.</p>
 * <p><i>Note:</i> it is key that an argument representing the event object is 
 * passed to the handler for browsers that do not support IE's <tt>window.event</tt>
 * object.</p>
 */
mi.getEventSrc = function (e) {
	if (!e) {e = window.event;}
	if (e.target) {
		return e.target;
	} else if (e.srcElement) {
		return e.srcElement;
	}
};


/**
 * Pattern used by {@link #templateParser} to find variables.
 * @type RegEx
 */
mi.templateVarPattern = /\@([^\@]+)\@/g;
/** method for parsing a template and replacing a pattern with the equivalent
 * attributes from an object
 *
 * <pre>var data object to get values from
 *var template string containing placeholders</pre>
 *
 * <p>Placeholders in the template should be given the name of the attribute to be 
 * used as the substitute surrounded by "@" symbols, i.e. @name@</p>
 *
 * <p>The pattern is defined outside of the method to avoid instantiating the 
 * pattern every time the method is used.</p>
 *
 * @param {Object} data Attributes should be the name of the variable to be searched for
 * and value is what will be put into the template.
 * @param {String} template The template string used to format the output.
 * @return The template with each variable replaced with the corresponding value from
 * the <i>data</i> argument.
 * @type String
 */
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
 *
 * @param {Object} sourceData Each attribute will be made into part of the resulting string.
 * @param {String} firstDelimiter Delimiter to be used between attributes.
 * @param {String} secondDelimiter Delimiter to be used between the name and the value.
 * @author Jamie Kirk
 * @type String
 */
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.');
	}
};

/**
 * Checks for a pageInfo object in the global namespace and loads any data, that
 * doesn't already exist, into the mi.pageInfo object.
 * <p>Any objects will be cloned, not referenced, and pre-existing values will 
 * not be overwritten.</p>
 * <p> This method only officially supports one nested object, 
 * i.e. pageInfo.asset.id. A second-level nested object may be created,
 * i.e., pageInfo.asset.foo.bar; however, pageInfo.asset.foo cannot then
 * accept additional attributes, nor can pageInfo.asset.foo.bar be overwritten.</p>
 * <p>The global object is nullified after loading is complete to encourage
 * accessing data in the mi object.</p>
 */
mi.loadPageInfo = function() {
	if (window.pageInfo) {
		var pi = window.pageInfo;
		if (this.pageInfo == undefined) {
			this.pageInfo = this.cloneObject(pi);
		} else {
			for (var key in pi) {
				if (key === 'version' && ( parseFloat(pi[key]) > parseFloat(this.pageInfo.version) ) ) {
					this.pageInfo.version = pi[key];
				} else if (this.pageInfo[key] == undefined) {
					this.pageInfo[key] = this.cloneObject(pi[key]);
				} else if (typeof this.pageInfo[key] == 'object') {
					for (var key2 in pi[key]) {
						this.pageInfo[key][key2] = (this.pageInfo[key][key2]) ? this.pageInfo[key][key2] : this.cloneObject(pi[key][key2]);
					}
				}
			}
		}
	}
	window.pageInfo = null;
}

/** method for ensuring that js executes only after the document is ready
 *
 * @param {Integer} time How long (in seconds) to wait for the document to render
 * @param {String} target A JQuery-type selector
 * @param {Object} callback The function to execute when the document is ready
 * @author Scot Billman
 */
mi.wait_for_ready = function( time, target, callback ){
   var checker, time_spent = 0, interval = 3000;

   _check_document = function(){
      if( null !== $(target) ){
         clearInterval( checker );
         callback();
      } else {
         time_spent += interval/1000;
         if( time_spent >= time ){
            clearInterval( checker );
         }
      }
   };

   $(document).ready( function() {
      checker = setInterval( _check_document, interval );
   });
};

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

/** MI_Cookie.js ****************************************************************
 * @fileoverview Class for managing cookies. This class allows you to interact with cookies
 * as an object with each named value represented as a property of the object.
 * This class will store multiple name/value pairs in a single cookie, reducing 
 * the number of cookies needed. Browsers may enforce limits to the number of 
 * individual cookies stored, so bundling values up in a single cookie is a good
 * idea. New cookies should be used if there's a difference in access rights, or
 * a cookie is getting too big, 4k of data is generally the limit.
 * @minify true
 * @author Joe Whetzel
 * @aggpath js/MI_Cookie.js
 *************************************************************************** */
 var mi = (!mi) ? {'media_domain':''} : mi;
 
/** Cookie object constructor. This constructor creates the javascript object, it
 * does not create the browser cookie, use {@link #store} to store the cookie in 
 * the browser.
 * @param document the Document object for which the cookie is stored
 * @param {String} name string that specifies a name for the cookie, defaults to "cookie"
 * @param {Integer} minutes how long until the cookie expires, defaults to current session
 * @param {String} path the path with which the cookie is associated, defaults to current page
 * @param {String} domain domain the cookie is associated to
 * @param {Boolean} secure whether or not the cookie is secure, only if the connection is secure
 * @constructor
 */
mi.Cookie = function (document, name, minutes, path, domain, secure) {
	/** Document object for which the cookie is stored. Default is to use the current document. */
	this.$document = (document) ? document : window.document;
	/** Name of the cookie. Defaults to "cookie".
	 * @type Document
	 */
	this.$name = (name) ? name : 'cookie';
	/** Minutes until the cookie expires. Default is at the end of the current session.
	 * @type Integer
	 */
	this.$expiration = (minutes) ? new Date((new Date()).getTime() + minutes * 60000) : null;
	/** Path associated with the cookie.
	 * @type String
	 */
	this.$path = (path) ? path : null;
	/** Domain associated with the cookie.
	 * @type String
	 */
	this.$domain = (domain) ? domain : null;
	/** Whether or not the cookie is secure.
	 * @type Boolean
	 */
	this.$secure = (secure) ? true : false;
};

/** Stores the cookie in the browser. Defining or changing values in the cookie
 * object alone does not save the values to the browser. After working with the
 * cookie object it must be stored in the browser.
 */
mi.Cookie.prototype.store = function() {
	var cookieVal = "";
	for(var prop in this) {
		if ((prop.charAt(0) == '$') || ((typeof this[prop]) == 'function')) {
			continue;
		}
		if (cookieVal !== "") {
			cookieVal += '&';
		}
		cookieVal += prop + ':' + escape(this[prop]);
	}
	var cookie = this.$name + '=' + cookieVal;
	cookie += (this.$expiration) ? '; expires=' + this.$expiration.toGMTString() : '';
	cookie += (this.$path) ? '; path=' + this.$path : '';
	cookie += (this.$domain) ? '; domain=' + this.$domain : '';
	cookie += (this.$secure) ? '; secure' : '';
	this.$document.cookie = cookie;
};

/** Loads a single cookie from the browser into the cookie object, making each 
 * name/value pair properties of the object.
 * @type Boolean
 */
mi.Cookie.prototype.load = function() {
	var allCookies = this.$document.cookie;
	if (allCookies === "") {
		return false;
	}
	var start = allCookies.indexOf(this.$name + '=');
	if (start == -1) {
		return false;
	}
	start += this.$name.length + 1;
	var end = allCookies.indexOf(';', start);
	if (end == -1) {
		end = allCookies.length;
	}
	var cookieVal = allCookies.substring(start, end);
	var a = cookieVal.split('&');
	if ((a.length == 1) && (a[0].indexOf(':') == -1)) {
		var prop = this.$name;
		this[prop] = unescape(cookieVal.replace(/\+/g, '%20')); // PHP encodes spaces with a '+'
		return true;
	}
	for(var i=0; i < a.length; i++) {
		a[i] = a[i].split(':');
	}
	for(i=0; i < a.length; i++) {
		this[a[i][0]] = unescape(a[i][1]);
	}
	return true;
};

/** Method for removing the entire cookie from the browser.
 */
mi.Cookie.prototype.remove = function() {
	var cookie = this.$name + '=';
	cookie += (this.$path) ? '; path=' + this.$path : '';
	cookie += (this.$domain) ? '; domain=' + this.$domain : '';
	cookie += '; expires=Fri, 02-Jan-1970 00:00:00 GMT';
	this.$document.cookie = cookie;
};



/* MI_Cookie.js ^ *********************************************************** */
/** MI_Commenting.js ***************************************************************
 * @fileOverview
 * A generic class for managing commenting functionality. This app should be
 * extended with an backend-specific extension.
 *
 * @minify true
 * @author Joe Whetzel (jwhetzel [at] mcclatchyinteractive.com)
 * @aggpath commenting/js/MI_Commenting.js
 */

/**
 * Commenting app constructor. This app functions as a generalized API for 
 * commenting functionality, a backend-specific extension is required.
 *
 * <p>All that is required to implement commenting on a page is:</p>
 * <ol>
 *   <li>A target div, default is to look for a div with an id of "commentingStage",</li>
 *   <li>Instantiate the commenting app, and</li>
 *   <li>Call the commenting app's display method.</li>
 * </ol>
 * <pre><div id='commentingStage'></div>
<script>
	mi.commenting = new mi.Commenting();
	mi.commenting.display();
</script></pre>
 *
 * <h3>Configuration options</h3>
 * <dl>
 *   <dt>accountName</dt>
 *   <dd>Name used by the backend to identify the site, default is derived from 
 *     the domain.</dd>
 *   <dt>enabled</dt>
 *   <dd>Integer value to enable/disable commenting, default is enabled.<br>
 *     0 = fully disabled<br>
 *     1 = fully enabled<br>
 *     2 = disable comment submission & display<br>
 *     3 = enable comment submission & display only<br>
 *     4 = disable popular threads widget<br>
 *   </dd>
 *   <dt>target</dt>
 *   <dd>Id value of the target element on the page in which the commenting 
 *     features are inserted, default is "commentingStage".</dd>
 * </dl>
 *
 * @constructor
 */
mi.Commenting = function() {
  mi.App.apply(this, arguments);
/**
 * @private
 */
   this._manageConf = function(prop, val) {
    switch (prop) {
      case 'enabled':
        var v = parseInt(val);
        if (isNaN(v)) {
					val = (val.toLowerCase) ? val.toLowerCase() : val;
        	switch (val) {
						case true:
						case 'true':
						case 'yes':
						case 'on':
							v = 1;
							break;
						default:
							v = 0;
							break;
        	}
        }
        val = v;
			default:
				break;
    }
    return val;
  };
  // without the ability to disable commenting globally commenting will default to disabled
  if (mi.control && mi.control.commenting != undefined) {
		this.setConf('enabled',mi.control.commenting);
  } else {
  	this.setConf('enabled',0);
  	console.warn('Commenting has been instantiated, but disabled because mi.control.commenting is not defined.');
  }
  mi.loadPageInfo();
  // account name is based on the domain, i.e. for www.mireference.com the account name is "mireference"
  var splitHost = window.location.host.split('.');
	this.setConf('accountName',splitHost[splitHost.length - 2]);
	this.setConf('target','commentingStage');
	this.finish();
};

/**
 * Hook used to add a process to the end of the constructor.
 *
 * <p>This is called by default, but out of the box  doesn't contain any functionality. Overwrite this method if you want to add your own functionality.</p>
 */
mi.Commenting.prototype.finish = function() {};

/**
 * Calling this method will add commenting features to the page.
 *
 * <p>For commenting to be successfully added the page must have a target element
 * present on the page.</p>
 */
mi.Commenting.prototype.display = function() {
	if(window.gomez && window.gomez.startInterval){window.gomez.startInterval('display commenting');}
	var e = this.getConf('enabled');
	if ( e !== 0 && e !== 2 ) {
		this._renderCommenting();
	} else {
		console.info('Submission and display of comments has been disabled.');
	}
	if(window.gomez && window.gomez.endInterval){window.gomez.endInterval('display commenting');}
};

mi.Commenting.prototype.displayPopular = function(count) {
	if(window.gomez && window.gomez.startInterval){window.gomez.startInterval('popular comment threads');}
	var e = this.getConf('enabled');
	if ( e !== 0 && e !== 3 && e !== 4 ) {
		this._displayPopular(count);
	} else {
		console.info('The popular comment threads widget has been disabled.');
	}
	if(window.gomez && window.gomez.endInterval){window.gomez.endInterval('popular comment threads');}
};

mi.Commenting.prototype.displayCommentCount = function() {
	if(window.gomez && window.gomez.startInterval){window.gomez.startInterval('comment count');}
	var e = this.getConf('enabled');
	if ( e !== 0 && e !== 2 ) {
		this._displayCommentCount();
  } else {
		console.info('Submission and display of comments has been disabled.');
  }
	if(window.gomez && window.gomez.endInterval){window.gomez.endInterval('comment count');}
}

/** ^ MI_Commenting.js ****************************************************** */
/** MI_Commenting_Disqus.js ****************************************************
 * @fileOverview
 * This is an extension to MI_Commenting.js that enables commenting via Disqus.
 *
 * @minify true
 * @author Joe Whetzel (jwhetzel [at] mcclatchyinteractive.com)
 * @aggpath commenting/js/MI_Commenting_Disqus.js
 */

mi.Commenting.prototype.extended = true;

/** Required to resolve some IE scoping issues.
*/
var disqus_identifier, disqus_shortname, disqus_remote_auth_s2, disqus_title;

/**
 * facebookXdReceiverPath used to prevent a conflict between Disqus and Facebook
 * Connect that can cause analytics issues. The variable will be defined when
 * Disqus commenting is instantiated. We define it here to avoid IE problems
 * declaring variables in the window after the page has loaded.
 */
if (typeof facebookXdReceiverPath == "undefined") {
	var facebookXdReceiverPath;
}

/**
 * Method that handles any of the Disqus specific processes required in adding
 * commenting features to a page. Both the input form and comments are included.
 *
 * <p>This method should not be called directly, instead it is called via the
   mi.Commenting.display method.</p>
 *
 * @private
 */

mi.Commenting.prototype._displayCommentingDisqus = function() {
	// global variables used by Disqus
	window.disqus_identifier = this.getThreadId();	// identifies thread
	var cookie = new mi.Cookie(document, 'disqus');
	cookie.load();
	window.disqus_remote_auth_s2 = cookie.disqus;	// single sign on
	window.disqus_title = mi.pageInfo.asset.title;

	if (window.disqus_identifier != undefined) {
		var target = document.getElementById(this.getConf('target'));

		// since a Disqus-specific element is targeted we'll handle creating and adding it to the configured target
		var thread = document.createElement('div');
		thread.id = 'disqus_thread';
		target.appendChild(thread);

		// add the call to Disqus' script
		var dsq = document.createElement('script');
		dsq.type = 'text/javascript';
		dsq.async = true;
		dsq.src = 'http://'+ this.getConf('accountName') +'.disqus.com/embed.js';
		(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);

                /* replace the disqus logout link if needed */
                mi.wait_for_ready( 15, this.getConf( 'selectorTarget' ), this.linkReplace() );
	} else {
		console.error('Commenting could not be loaded because there was no defined thread id.');
	}
};
// a non-vendor-specific method name is used so that vendors can be changed
// vendor-specific names used in extensions so that multiple vendors can be present
mi.Commenting.prototype._renderCommenting = mi.Commenting.prototype._displayCommentingDisqus;
/**
 * Writes the Disqus Popular Threads widget. WARNING: makes use of document.write().
 * @private
 */
mi.Commenting.prototype._displayPopularDisqus = function(count) {
	count = (isNaN(count)) ? this.getConf('discoveryCount') : count;
	if (isNaN(count)) {
		count = 0;
	}
	count = (count > 0 && count < 21) ? Math.floor(count) : 5;
	document.write('<script type="text/javascript" src="http://disqus.com/forums/' + this.getConf('accountName') + '/popular_threads_widget.js?num_items='+ count +'"></script>');
};
mi.Commenting.prototype._displayPopular = mi.Commenting.prototype._displayPopularDisqus;
/**
 * Writes the number of comments to a specified a tag. WARNING: makes use of document.write().
 * @private
 */
mi.Commenting.prototype._displayCommentCountDisqus = function() {
	window.disqus_identifier = this.getThreadId();	// identifies thread
	window.disqus_shortname = this.getConf('accountName');	// identifies site
  document.getElementById('commentCount').href = document.getElementById('commentCount').href + '#disqus_thread';
  document.getElementById('commentCount').setAttribute('data-disqus-identifier', this.getThreadId());

  var s = document.createElement('script'); s.async = true;
  s.src = 'http://disqus.com/forums/' + this.getConf('accountName') + '/count.js';
  (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
};
mi.Commenting.prototype._displayCommentCount = mi.Commenting.prototype._displayCommentCountDisqus;
/**
 * Method used to construct the id used to uniquely identify threads for an asset.
 */
mi.Commenting.prototype.getThreadId = function() {
	return (mi.pageInfo && mi.pageInfo.asset && mi.pageInfo.asset.id) ? mi.pageInfo.asset.id : undefined;
};

/**
 *  Method to replace the Disqus comment widget logout link
 *  with the InSite logout link. The 'target' and 'source' are
 *  JQuery selectors. The 'target' is delivered by Disqus,
 *  it should always be that value. The 'source' may have a
 *  different value if there is a non-standard install.
 */
mi.Commenting.prototype.linkReplace = function() {
   var obj = this;

   return( function(){
      if( !obj.getConf( 'selectorTarget' ) ){
         obj.setConf( 'selectorTarget', '.dsq-request-user-logout' );
      }
      if( !obj.getConf( 'selectorSource' ) ){
         obj.setConf( 'selectorSource', '#pluckLogOut' );
      }
      var target = $( obj.getConf( 'selectorTarget' ) );
      var source = $( obj.getConf( 'selectorSource' ) );
      if ( null !== source && null !== target && 'Guest' != account_user_name ){
         var s = source.clone( 1 );
         s.attr( 'class', target.attr( 'class' ) );
         target.replaceWith( s );
      }
   });
};

/**
 * Overwrites the Commenting app's default, empty finish method.
 * 
 * <p>This is being used to set the facebookXdReceiverPath variable to avoid
   Facebook Connect conflicts with Disqus.</p>
 */
mi.Commenting.prototype.finish = function() {
	window.facebookXdReceiverPath = '/static/scripts/mi/third_party/facebook/fb-disqus_xd_receiver.html';
}


/** ^ MI_Commenting_Disqus.js *********************************************** */
 mi.commenting = new mi.Commenting();
 // If necessary, you can add configuration overrides here.
 var disqus_config = function () {
   this.sso = {
     name:    "Cary News",
     button:  "http://media.carynews.com/static/images/dsq-login-button-mi.png",
     url:     "http://www.carynews.com/static/insite/disqus_login.html",
     logout:  "http://www.carynews.com/reg-bin/tint.cgi?mode=logout",
     width:   "600",
     height:  "375"
   };
 };


