/**
 *	The 'page' module does stuff.
 *
 *	@module page
 *	@requires classy, kizzy, signals, hasher, loadmask, json2
 */
 
define('page',
[
	'classy',
	'kizzy',
	'signals',
	'hasher',
	'loadmask',
	'json2'
],
function(
	Class,
	kizzy,
	signals,
	hasher
)
{
	/**
	 *	Class for the {{#crossLinkModule "page"}}page{{/crossLinkModule}} Module
	 * 	@class Page
	 *	@extends Class
	 *	@constructor 
	 */
	return Class.$extend({
		
		/**
		 *	Constructor Function for page
		 *	@method __init__
		 *	@param {String} name The name for the page.
		 */
		__init__ : function(name){
			this.name = name;
			this.settings = kizzy(this.name);
			this.loadSignal = new signals.Signal();
			this.unloadSignal = new signals.Signal();
		},
		
		load : function(request){
			this.settings.clearExpireds();
			this.$class.pushRequest(request);
			this.loadSignal.dispatch(request);
			this.isLoaded = true;
		},
		
		unload : function(){
			this.settings.clearExpireds();
			this.unloadSignal.dispatch();
			this.isLoaded = false;
		},
		
		toString : function(){
			return '[Page ' + this.name + ']';
		},
		__classvars__ :{
			
			requests : [],
			
			setHash : function(hash){
				hasher.setHash(hash);
			},
			
			showLoadMask : function(message, container){
				container = container || $('#pagecontent');
				message = '<span class="loading">' + (message || this.LOADING_MESSAGE) + '</span>';
				container.mask(message);
			},
			
			hideLoadMask : function(container){
				container = container || $('#pagecontent');
				container.unmask();
			},
			
			pushRequest : function(request){
				if(this.requests.length >= 10)
					this.requests.shift();
				this.requests.push(request);
			},
			
			errorHandler : function(info, errorUrl){
				var container = $('#pagecontent');
				if(info && errorUrl){
					var json = this.generateErrorJSON(info);
					$.ajax({
						'url' : errorUrl,
						'type' : 'POST',
						'data' : {
							'json' : json
						}
					});
				}
				var message = 'An error has occurred, and has been logged.  ' +
				'</br>If refreshing the page does not solve the issue, ' +
				'</br>try returning to the main page ' +
				'and making</br> another selection';
				container.mask(message);
			},

			clearErrorMessage : function(){
				var container = $('#pagecontent');
				container.unmask();
			},
			
			generateErrorJSON : function(info){		
				return JSON.stringify($.extend(true, {}, info, {
					'browser' : $.browser,
					'requests' : this.requests
				}));
			},
			
			LOADING_MESSAGE : 'Loading...'
		}
	});
});

define('js/Facade',[
	'puremvc'
], function(
	puremvc
){
	return puremvc.Facade.$extend({
		
		sendNotification : function(name, body, type){
			// Log the notification
			if(typeof console !== 'undefined' && typeof console.log == 'function')
				console.log(Date.now() + ' : ' + name + (type ? ' : ' + type : ''));
				
			try{
				this.$super(name, body, type);
			}
			catch(e){
				// Log the error
				if(typeof console !== 'undefined' && typeof console.log == 'function')
					console.log('ERROR: ' + e.name + ' : ' + e.message);
					
				if(location.host.indexOf("wwwdev") != -1  || location.host.indexOf("vdc") != -1) {
					throw e;
				}
				else {
					this.$super(this.$class.ERROR, e, name);
				}
			}
		},
		
		__classvars__ : {
			
			///////////////////
			// Notifications //
			///////////////////
			
			// Startup, Reload, Unload
			
			STARTUP : 'startup',
			
			RELOAD : 'reload',
			
			UNLOAD : 'unload',
			
			// Parameter Updates
			
			TOPIC_PARAMS_UPDATED : 'topicParamsUpdated',
			
			TABLE_PARAMS_UPDATED : 'tableParamsUpdated',
			
			CHART_PARAMS_UPDATED : 'chartParamsUpdated',
			
			CTYPE_PARAM_UPDATED : 'ctypeParamUpdated',
			
			MAP_TYPE_PARAM_UPDATED : 'mapTypeParamUpdated',
			
			TIME_PARAMS_UPDATED : 'timeParamsUpdated',
			
			DEFAULT_CHART_SET : 'defaultChartSet',
			
			RSE_PARAM_UPDATED : 'rseParameterUpdated',
			
			PIN_KEY_PARAM_UPDATED : 'pinKeyParameterUpdated',
			
			REGION_TYPE_PARAM_UPDATED : 'regionTypeParameterUpdated',
			
			// Data Updates
			
			CHART_DATA_UPDATED : 'chartDataUpdated',
			
			TABLEDATA_UPDATED : 'tableDataUpdated',
			
			MAP_DATA_UPDATED : 'mapDataUpdated',
			
			CSV_DATA_GENERATED : 'csvDownloadDataGenerated',
			
			// Selection Updates
			
			CHART_SELECTION_UPDATED : 'chartSelectionUpdated',
			
			FREQUENCY_SELECTION_UPDATED : 'frequencySelectionUpdated',
			
			TABLE_SELECTION_UPDATED : 'tableSelectionUpdated',
			
			TOPIC_SELECTION_UPDATED : 'topicSelectionUpdated',
			
			REPORT_SELECTION_UPDATED : 'reportSelectionUpdated',
			
			TIME_SELECTION_UPDATED : 'timeSelectionUpdated',
			
			DOWNLOAD_OPTION_SELECTED : 'downloadOptionSelected',
			
			SEARCH_STRING_UPDATED : 'searchStringUpdated',
			
			MAP_TYPE_SELECTION_UPDATED : 'mapTypeSelectionUpdated',
			
			PINNED_SERIES_UPDATED : 'pinnedSeriesUpdated',
			
			RSE_SELECTION_UPDATED : 'rseSelectionUpdated',
			
			PIN_KEY_SELECTION_UPDATED : 'pinKeySelected',
			
			REGION_TYPE_SELECTION_UPDATED : 'regionTypeSelected',
			
			// Map Click
			
			STATE_MAP_CLICKED : 'stateMapClicked',
			
			// Page click through
			
			AGGREGATE_DATA_CLICK_THROUGH : 'aggregateDataClickThrough',

			PLANT_LIST_CLICK_THROUGH : 'plantListClickThrough',
			
			MINE_LIST_CLICK_THROUGH : 'mineListClickThrough',
			
			SHIPMENT_LIST_CLICK_THROUGH : 'shipmentListClickThrough',
			
			POWER_PLANT_HIGHLIGHT : 'powerPlantHighlight',
			
			MINE_HIGHLIGHT : 'mineHighlight',
			
			// View state changes
			
			TABLE_RENDERED : 'tableRendered',
			
			
			UPDATE_VISUALIZATION_VIEW_STACK : 'updateVisualizationViewStack',
			
			IMPORT_EXPORT_PORT_SELECTED : 'importExportPortSelected',
			
			IMPORT_EXPORT_COUNTRY_SELECTED : 'importExportCountrySelected',
			
			SOURCE_KEY_SELECTED : 'sourceKeySelected',
			
			// Error state
			
			ERROR : 'error'
		}
	});
});
/**
 * @license RequireJS text 2.0.10 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/text for details
 */
/*jslint regexp: true */
/*global require, XMLHttpRequest, ActiveXObject,
  define, window, process, Packages,
  java, location, Components, FileUtils */

define('text',['module'], function (module) {
    'use strict';

    var text, fs, Cc, Ci, xpcIsWindows,
        progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
        xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
        bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
        hasLocation = typeof location !== 'undefined' && location.href,
        defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
        defaultHostName = hasLocation && location.hostname,
        defaultPort = hasLocation && (location.port || undefined),
        buildMap = {},
        masterConfig = (module.config && module.config()) || {};

    text = {
        version: '2.0.10',

        strip: function (content) {
            //Strips <?xml ...?> declarations so that external SVG and XML
            //documents can be added to a document without worry. Also, if the string
            //is an HTML document, only the part inside the body tag is returned.
            if (content) {
                content = content.replace(xmlRegExp, "");
                var matches = content.match(bodyRegExp);
                if (matches) {
                    content = matches[1];
                }
            } else {
                content = "";
            }
            return content;
        },

        jsEscape: function (content) {
            return content.replace(/(['\\])/g, '\\$1')
                .replace(/[\f]/g, "\\f")
                .replace(/[\b]/g, "\\b")
                .replace(/[\n]/g, "\\n")
                .replace(/[\t]/g, "\\t")
                .replace(/[\r]/g, "\\r")
                .replace(/[\u2028]/g, "\\u2028")
                .replace(/[\u2029]/g, "\\u2029");
        },

        createXhr: masterConfig.createXhr || function () {
            //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
            var xhr, i, progId;
            if (typeof XMLHttpRequest !== "undefined") {
                return new XMLHttpRequest();
            } else if (typeof ActiveXObject !== "undefined") {
                for (i = 0; i < 3; i += 1) {
                    progId = progIds[i];
                    try {
                        xhr = new ActiveXObject(progId);
                    } catch (e) {}

                    if (xhr) {
                        progIds = [progId];  // so faster next time
                        break;
                    }
                }
            }

            return xhr;
        },

        /**
         * Parses a resource name into its component parts. Resource names
         * look like: module/name.ext!strip, where the !strip part is
         * optional.
         * @param {String} name the resource name
         * @returns {Object} with properties "moduleName", "ext" and "strip"
         * where strip is a boolean.
         */
        parseName: function (name) {
            var modName, ext, temp,
                strip = false,
                index = name.indexOf("."),
                isRelative = name.indexOf('./') === 0 ||
                             name.indexOf('../') === 0;

            if (index !== -1 && (!isRelative || index > 1)) {
                modName = name.substring(0, index);
                ext = name.substring(index + 1, name.length);
            } else {
                modName = name;
            }

            temp = ext || modName;
            index = temp.indexOf("!");
            if (index !== -1) {
                //Pull off the strip arg.
                strip = temp.substring(index + 1) === "strip";
                temp = temp.substring(0, index);
                if (ext) {
                    ext = temp;
                } else {
                    modName = temp;
                }
            }

            return {
                moduleName: modName,
                ext: ext,
                strip: strip
            };
        },

        xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,

        /**
         * Is an URL on another domain. Only works for browser use, returns
         * false in non-browser environments. Only used to know if an
         * optimized .js version of a text resource should be loaded
         * instead.
         * @param {String} url
         * @returns Boolean
         */
        useXhr: function (url, protocol, hostname, port) {
            var uProtocol, uHostName, uPort,
                match = text.xdRegExp.exec(url);
            if (!match) {
                return true;
            }
            uProtocol = match[2];
            uHostName = match[3];

            uHostName = uHostName.split(':');
            uPort = uHostName[1];
            uHostName = uHostName[0];

            return (!uProtocol || uProtocol === protocol) &&
                   (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
                   ((!uPort && !uHostName) || uPort === port);
        },

        finishLoad: function (name, strip, content, onLoad) {
            content = strip ? text.strip(content) : content;
            if (masterConfig.isBuild) {
                buildMap[name] = content;
            }
            onLoad(content);
        },

        load: function (name, req, onLoad, config) {
            //Name has format: some.module.filext!strip
            //The strip part is optional.
            //if strip is present, then that means only get the string contents
            //inside a body tag in an HTML string. For XML/SVG content it means
            //removing the <?xml ...?> declarations so the content can be inserted
            //into the current doc without problems.

            // Do not bother with the work if a build and text will
            // not be inlined.
            if (config.isBuild && !config.inlineText) {
                onLoad();
                return;
            }

            masterConfig.isBuild = config.isBuild;

            var parsed = text.parseName(name),
                nonStripName = parsed.moduleName +
                    (parsed.ext ? '.' + parsed.ext : ''),
                url = req.toUrl(nonStripName),
                useXhr = (masterConfig.useXhr) ||
                         text.useXhr;

            // Do not load if it is an empty: url
            if (url.indexOf('empty:') === 0) {
                onLoad();
                return;
            }

            //Load the text. Use XHR if possible and in a browser.
            if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
                text.get(url, function (content) {
                    text.finishLoad(name, parsed.strip, content, onLoad);
                }, function (err) {
                    if (onLoad.error) {
                        onLoad.error(err);
                    }
                });
            } else {
                //Need to fetch the resource across domains. Assume
                //the resource has been optimized into a JS module. Fetch
                //by the module name + extension, but do not include the
                //!strip part to avoid file system issues.
                req([nonStripName], function (content) {
                    text.finishLoad(parsed.moduleName + '.' + parsed.ext,
                                    parsed.strip, content, onLoad);
                });
            }
        },

        write: function (pluginName, moduleName, write, config) {
            if (buildMap.hasOwnProperty(moduleName)) {
                var content = text.jsEscape(buildMap[moduleName]);
                write.asModule(pluginName + "!" + moduleName,
                               "define(function () { return '" +
                                   content +
                               "';});\n");
            }
        },

        writeFile: function (pluginName, moduleName, req, write, config) {
            var parsed = text.parseName(moduleName),
                extPart = parsed.ext ? '.' + parsed.ext : '',
                nonStripName = parsed.moduleName + extPart,
                //Use a '.js' file name so that it indicates it is a
                //script that can be loaded across domains.
                fileName = req.toUrl(parsed.moduleName + extPart) + '.js';

            //Leverage own load() method to load plugin value, but only
            //write out values that do not have the strip argument,
            //to avoid any potential issues with ! in file names.
            text.load(nonStripName, req, function (value) {
                //Use own write() method to construct full module value.
                //But need to create shell that translates writeFile's
                //write() to the right interface.
                var textWrite = function (contents) {
                    return write(fileName, contents);
                };
                textWrite.asModule = function (moduleName, contents) {
                    return write.asModule(moduleName, fileName, contents);
                };

                text.write(pluginName, nonStripName, textWrite, config);
            }, config);
        }
    };

    if (masterConfig.env === 'node' || (!masterConfig.env &&
            typeof process !== "undefined" &&
            process.versions &&
            !!process.versions.node &&
            !process.versions['node-webkit'])) {
        //Using special require.nodeRequire, something added by r.js.
        fs = require.nodeRequire('fs');

        text.get = function (url, callback, errback) {
            try {
                var file = fs.readFileSync(url, 'utf8');
                //Remove BOM (Byte Mark Order) from utf8 files if it is there.
                if (file.indexOf('\uFEFF') === 0) {
                    file = file.substring(1);
                }
                callback(file);
            } catch (e) {
                errback(e);
            }
        };
    } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
            text.createXhr())) {
        text.get = function (url, callback, errback, headers) {
            var xhr = text.createXhr(), header;
            xhr.open('GET', url, true);

            //Allow plugins direct access to xhr headers
            if (headers) {
                for (header in headers) {
                    if (headers.hasOwnProperty(header)) {
                        xhr.setRequestHeader(header.toLowerCase(), headers[header]);
                    }
                }
            }

            //Allow overrides specified in config
            if (masterConfig.onXhr) {
                masterConfig.onXhr(xhr, url);
            }

            xhr.onreadystatechange = function (evt) {
                var status, err;
                //Do not explicitly handle errors, those should be
                //visible via console output in the browser.
                if (xhr.readyState === 4) {
                    status = xhr.status;
                    if (status > 399 && status < 600) {
                        //An http 4xx or 5xx error. Signal an error.
                        err = new Error(url + ' HTTP status: ' + status);
                        err.xhr = xhr;
                        errback(err);
                    } else {
                        callback(xhr.responseText);
                    }

                    if (masterConfig.onXhrComplete) {
                        masterConfig.onXhrComplete(xhr, url);
                    }
                }
            };
            xhr.send(null);
        };
    } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
            typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
        //Why Java, why is this so awkward?
        text.get = function (url, callback) {
            var stringBuffer, line,
                encoding = "utf-8",
                file = new java.io.File(url),
                lineSeparator = java.lang.System.getProperty("line.separator"),
                input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
                content = '';
            try {
                stringBuffer = new java.lang.StringBuffer();
                line = input.readLine();

                // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
                // http://www.unicode.org/faq/utf_bom.html

                // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
                // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
                if (line && line.length() && line.charAt(0) === 0xfeff) {
                    // Eat the BOM, since we've already found the encoding on this file,
                    // and we plan to concatenating this buffer with others; the BOM should
                    // only appear at the top of a file.
                    line = line.substring(1);
                }

                if (line !== null) {
                    stringBuffer.append(line);
                }

                while ((line = input.readLine()) !== null) {
                    stringBuffer.append(lineSeparator);
                    stringBuffer.append(line);
                }
                //Make sure we return a JavaScript string and not a Java string.
                content = String(stringBuffer.toString()); //String
            } finally {
                input.close();
            }
            callback(content);
        };
    } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
            typeof Components !== 'undefined' && Components.classes &&
            Components.interfaces)) {
        //Avert your gaze!
        Cc = Components.classes,
        Ci = Components.interfaces;
        Components.utils['import']('resource://gre/modules/FileUtils.jsm');
        xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);

        text.get = function (url, callback) {
            var inStream, convertStream, fileObj,
                readData = {};

            if (xpcIsWindows) {
                url = url.replace(/\//g, '\\');
            }

            fileObj = new FileUtils.File(url);

            //XPCOM, you so crazy
            try {
                inStream = Cc['@mozilla.org/network/file-input-stream;1']
                           .createInstance(Ci.nsIFileInputStream);
                inStream.init(fileObj, 1, 0, false);

                convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
                                .createInstance(Ci.nsIConverterInputStream);
                convertStream.init(inStream, "utf-8", inStream.available(),
                Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);

                convertStream.readString(inStream.available(), readData);
                convertStream.close();
                inStream.close();
                callback(readData.value);
            } catch (e) {
                throw new Error((fileObj && fileObj.path || '') + ': ' + e);
            }
        };
    }
    return text;
});

define('js/model/ConfigProxy',[
	'puremvc',
	'text!configJSON',
	'json2'
], function(
	puremvc,
	configJSON
){
	var config = JSON.parse(configJSON);
	return  puremvc.Proxy.$extend({
		
		topicMap : null,
		
		__init__ : function(){
			var combinedConfig = {
				'reports' : config.reports,
				'topics' : config.topics
			};
			for(var i = 0; i < combinedConfig.topics.length; i++) {
				for(var j = 0; j < combinedConfig.topics[i].aggColumns.length; j++) {
					combinedConfig.topics[i].aggColumns[j].filterGroup = config.filterGroups[combinedConfig.topics[i].aggColumns[j].filterGroupId];
				}
			}
			this.$super(this.$class.NAME, combinedConfig);
			this.generateTopicMap();
		},
		
		generateTopicMap : function(){
			var ret = {},
			i, topic;
			
			for(i=0; i < this.getTopics().length; i++){
				topic = this.getTopics()[i];
				ret[topic['dataTopicId']] = topic;
			}
			
			this.topicMap = ret;
		},
		
		getTopics : function(){
			return this.getData().topics;
		},
		
		getReports : function(){
			return this.getData().reports;
		},
		
		getTopic : function(topicId){
			return this.topicMap[topicId];
		},
		
		getFilterQuery : function(topicId, seriesId) {
			var aggColumns = this.getTopic(topicId).aggColumns,
			seriesIdSplit = seriesId.split('.')[2].split('-'),
			ret = {}, 
			i, j, k, q, filterValues, valIndex, checkedArray;
			
			for(i = 0; i < aggColumns.length; i++) {
				filterValues = aggColumns[i].filterGroup.filterValues;
				
				for(j = 0; j < filterValues.length; j++) {
					if(filterValues[j].filterValue == seriesIdSplit[i]) {
						valIndex = filterValues[j].valueId;
						break;
					}
				}
				
				checkedArray = new Array(1);
				checkedArray[valIndex] = 1;
				
				ret[aggColumns[i].filterGroup.varName] = '';
				
				var binaryString = "";
				for(var k = 0; k < Math.ceil(checkedArray.length / 5) * 5; k++) {
					if(checkedArray[k] && checkedArray[k] == 1) {
						binaryString += "1";
					}
					else {
						binaryString += "0";
					}
					if((k % 5) == 4) {
						ret[aggColumns[i].filterGroup.varName] = ret[aggColumns[i].filterGroup.varName] + parseInt(binaryString, 2).toString(32);
						binaryString = "";
					}
				}
				if(ret[aggColumns[i].filterGroup.varName] == '0')
					return null;
				else
					ret[aggColumns[i].filterGroup.varName] = (ret[aggColumns[i].filterGroup.varName] == "") ? "g" : ret[aggColumns[i].filterGroup.varName];
			
			}
			q = '';
			
			for(i in ret)
				if(ret.hasOwnProperty(i))
					q += i + '=' + ret[i] + '&';
			
			return q.substring(0, q.length - 1);
		},
		
		__classvars__ : {
			NAME : 'ConfigProxy'
		}
	});
});
define('js/model/RequestProxy',[
	'puremvc'
], function(
	puremvc
){
	return puremvc.Proxy.$extend({
		__init__ : function(request){
			this.$super(this.$class.NAME, request);
		},
		
		getQuery : function(){
			return this.getData()['query'];
		},
		
		getPath : function(){
			var pattern = this.getData()['request_'], i;
			if(pattern.indexOf('/') != -1) {
				if(pattern.indexOf('?') != -1) {
					i = Math.min(pattern.indexOf('/'), pattern.indexOf('/'));
				}
				else {
					i = pattern.indexOf('/');
				}
			}
			else
			{
				if(pattern.indexOf('?') != -1) {
					i = pattern.indexOf('?');
				}
				else {
					i = pattern.length;
				}
			}
			return pattern.substring(0, i);
		},
		
		getTopicId : function(){
			return this.getData()['0'];
		},
		
		__classvars__ : {
			NAME : 'RequestProxy'
		}
	});
});
	
define('js/model/ParameterProxy',[
	'puremvc'
], function(
	puremvc
){
	return puremvc.Proxy.$extend({
		
		__init__ : function(){
			this.$super(this.$class.NAME, {
				'query' : {}
			});
		},
		
		getQuery : function(){
			return this.getData()['query'];	
		},
		
		getQueryString : function(params){
			var query = $.extend({}, this.getQuery(), params),
			q = '?', keys = this.getQueryKeys(),
			i, key;
			
			for(i = 0; i < keys.length; i++){
				key = keys[i];
				if(query.hasOwnProperty(key))
					q += key + '=' + query[key] + '&';
			}
			$.each(query, function(key, value) {
				if($.inArray(key, keys) == -1) {
					q+=key + '=' + value + '&';
				}
			});
			return q.substring(0, q.length - 1);
		},
		
		copyQuery : function(tmpQ, newQ, oldQ, changeCallback){
			var changedKeys = {}, sameKeys = {}, key, tmpKey;
			
			// Loop over the keys, call the callback if any have changed
			
			for(key in newQ){
				if(newQ.hasOwnProperty(key)){
					tmpKey = key.toLowerCase();
					tmpQ[tmpKey] = newQ[key];
					if(oldQ[tmpKey] != newQ[key])
						changedKeys[tmpKey] = key;
					else
						sameKeys[tmpKey] = key;
				}
			}
			for(tmpKey in oldQ){
				if(oldQ.hasOwnProperty(tmpKey)){
					if(!(tmpKey in changedKeys) && !(tmpKey in sameKeys)){
						tmpQ[tmpKey] = void 0;
						changedKeys[tmpKey] = tmpKey;
					}
				}
			}
			
			for(tmpKey in changedKeys){
				if(changedKeys.hasOwnProperty(tmpKey)){
					changeCallback.call(this, tmpKey, tmpQ[tmpKey], oldQ[tmpKey]);
				}
			}
		},
		
		updateQuery : function(query, valChangedCallback){
			var tmp = {};
			
			this.copyQuery(tmp, query, this.getQuery(), valChangedCallback);
			
			this.validateQuery(tmp);
			
			$.extend(this.getData(), {
				'query' : tmp
			});
		},
		
		update : function(){
			// Implemented by subclasses
		},
		
		validateQuery : function(tmp){
			// Implemented by subclasses
		},
		
		getQueryKeys : function(){
			// Implemented by subclasses
			return [];
		},
		
		getGAString : function(params) {
			// Implemented by subclasses
			return '';
		},
		
		checkVal : function(obj, key, val, validator){
			obj[key] = obj[key] !== void 0 &&
				(validator ? validator(obj[key]) : true) ?
				obj[key] : val;
		},
		
		__classvars__ : {
			NAME : 'ParameterProxy'
		}
	});
});
define('js/model/DataProxy',[
	'puremvc'
], function(
	puremvc
){
	return puremvc.Proxy.$extend({
		
		__init__ : function(){
			this.$super(this.$class.NAME);
			this.cache = {};
			this.cacheKeys = [];
		},
		
		setData : function(key, data){
			this.$super(data);
			this.addToCache(key, data);
		},
		
		setDataFromCache : function(key){
			this.data = this.getFromCache(key);
		},
		
		inCache : function(key){
			return key in this.cache;
		},
		
		addToCache : function(key, data){
			if(this.inCache(key))
				this.cache[key] = data;
			else{
				if(this.cacheKeys.length >= this.$class.CACHE_LIMIT){
					delete this.cache[this.cacheKeys.shift()];
				}
				
				this.cacheKeys.push(key);
				this.cache[key] = data;
			}
		},
		
		getFromCache : function(key){
			return this.cache[key];
		},
		
		__classvars__ : {
			CACHE_LIMIT : 5
		}
	});
});
define('js/model/TableDataProxy',[
	'js/model/DataProxy'
], function(
	DataProxy
){
	return DataProxy.$extend({
		
		__classvars__ : {
			
			NAME : 'TableDataProxy',
			
			getItemMetadata : function(row){
				var ret = {
					cssClasses : ''
				},
				datum = this[row];
				
				ret.cssClasses += 'level_' + datum['LEVEL'];
				if(!datum['HAS_DATA'])
					ret.cssClasses += ' NO_DATA';
					
				return ret;
			}
		}
	});
});
	
define('js/Controller',[
	'puremvc',
	'page',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'json2'
], function(
	puremvc,
	Page,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy
){
	//////////////
	// Commands //
	//////////////
	
	var ErrorCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var e = note.getBody(),
			requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);

			var jsonData = JSON.stringify(e);
			var route = requestProxy ? requestProxy.getData() : null;
			var notification = note.getType();
			var error = e.name + ' ' + e.message;
			var stack = e.stack;
			var logStr = '&route='+route+'¬ification='+notification+'&error='+error+'&stack='+stack;

			Page.errorHandler(e ? {
				'route' : requestProxy ? requestProxy.getData() : null,
				'notification' : note.getType(),
				'error' : e.name + ' ' + e.message,
				'stack' : e.stack
			} : void 0, 'data/errorService.php?method=logError'+logStr)
		}
	});
	
	var UpdateRequestProxyCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			requestProxy.setData(note.getBody());
		}
	});
	
	var UpdateParameterProxyCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			parameterProxy.update();
		}
	});
	
	var UpdateTableDataProxyCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			tableDataProxy.loadTableData();
		}
	});
	
	var UpdateChartedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			parameterProxy.resetCharted(note.getType());
			parameterProxy.updateCharted(parameterProxy.getUpdatedCharted(note.getBody(), note.getType()), note.getType());
		}
	});
	
	var UpdateChartDataCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			if(!tableDataProxy.isUpdating)
				tableDataProxy.updateChartData();
		}
	});
	
	var ShowLoadMaskCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			Page.showLoadMask();
		}
	});
	
	var HideLoadMaskCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			Page.hideLoadMask();
		}
	});
	
	var UpdateHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			params = note.getBody(),
			type = note.getType();
			
			if(type) {
				//var label = parameterProxy.getFullPath(params);
				var label = parameterProxy.getGAString(params);
				
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}
			Page.setHash(parameterProxy.getFullPath(params));
		}
	});
	
	var UpdateTopicHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			topicId = note.getBody()['topic'],
			params = note.getBody()['params'] || {},
			type = note.getType();
			
			if(type) {
				//var label = parameterProxy.getFullTopicPath({}, topicId);
				var label = note.getBody()['label'] || parameterProxy.getGAStringByTopic({}, topicId);
				
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}
			Page.setHash(parameterProxy.getFullTopicPath(params, topicId));
		}
	});
	
	var UpdateReportHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var paramsObj = note.getBody(),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			type = note.getType();
			
			if(type) {
				//var label = parameterProxy.getFullTopicPath(paramsObj.query, paramsObj.topic);
				var label = parameterProxy.getGAStringByTopic(paramsObj.query, paramsObj.topic);
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}
			Page.setHash(parameterProxy.getFullTopicPath(paramsObj.query, paramsObj.topic));
		}
	});
	
	var UpdatePlantHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var paramsObj = note.getBody(),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			pinned = parameterProxy.getCharted('pin'),
			type = note.getType();
			
			if(type) {
				//var label = parameterProxy.getFullTopicPath(paramsObj.query, paramsObj.topic);
				var label = paramsObj.label;
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}

			Page.setHash('plant/' + paramsObj.plantCode + '/?freq=' + paramsObj.freq + (pinned !== void 0 ? '&pin=' + pinned : ''));
		}
	});
	
	var UpdateMineHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var paramsObj = note.getBody(),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			pinned = parameterProxy.getCharted('pin'),
			type = note.getType();
			
			if(type) {
				var label = paramsObj.label;
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}

			Page.setHash('mine/' + paramsObj.code + '/?freq=' + paramsObj.freq + (pinned !== void 0 ? '&pin=' + pinned : ''));
		}
	});
	
	var UpdateShipmentHashCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var paramsObj = note.getBody(),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			pinned = parameterProxy.getCharted('pin'),
			type = note.getType();
			
			if(type) {
				var label = paramsObj.label;
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}

			Page.setHash('shipments/' + paramsObj.type + "/" + paramsObj.code + '/?freq=' + paramsObj.freq + (pinned !== void 0 ? '&pin=' + pinned : ''));
		}
	});
	
	var OutputToCSVCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			input1, input2,
			type = note.getType();
			
			if(type) {
				var label = parameterProxy.getGAString({});
				
				if(typeof _gaq != 'undefined') {
					_gaq.push(['_trackEvent', 'Enerdat', type, label]);
				}
				else if(typeof console !== 'undefined' && typeof console.log == 'function') {
					console.log('_trackEvent : Enerdat : ' + type + ' : ' + label);
				}
			}
			
			input1 = $('<input type="hidden" name="csvArr"/>').val(JSON.stringify(note.getBody()));
			input2 = $('<input type="hidden" name="fileName"/>').val(tableDataProxy.getTableTitle().replace(/,/g, '').replace(/\s+/g, '_'));
			
			jQuery('<form style="display:none;" action="'+ this.$class.POST_URL +'" method="post"></form>')
			.append(input1).append(input2) .appendTo('body').submit().remove();
		},
		
		__classvars__ : {
			POST_URL : '/global/scripts/jquery/highcharts/exporting-server/csv_exporter.php'
		}
	});

	var OpenAPIWindowCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			window.open('/opendata/series.php?sdid=' + note.getBody());
		}
	});
	
	var ResetChartedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			parameterProxy.resetCharted();
		}
	});
	
	var ResetChartindexedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			parameterProxy.resetChartindexed();
		}
	});
	
	var ResetStartEndCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topicId = note.getBody()['topic'],
			topic = configProxy.getTopic(topicId);
			
			if(!topic || !topic['selectDateRange'])
				parameterProxy.resetStartEnd();
		}
	});
	
	var ResetDatecodeCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topicId = note.getBody()['topic'],
			topic = configProxy.getTopic(topicId);
			
			if(!topic || topic['selectDateRange'])
				parameterProxy.resetDatecode();
		}
	});
	
	var ResetAggColumnsCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			parameterProxy.resetAggColumns(note.getBody()['topic']);
		}
	});
	
	var UpdateParameterFrequencies = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var body = note.getBody();
			parameterProxy.updateChartIdFrequencies(body['freq']);
		}
	});
	
	var TableParamsUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(ShowLoadMaskCommand);
			this.addSubCommand(UpdateTableDataProxyCommand);
		}
	});
	
	
	var FrequencySelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(UpdateParameterFrequencies);
			this.addSubCommand(ResetStartEndCommand);
			this.addSubCommand(UpdateHashCommand);
		}
	});
	
	return {
		'ErrorCommand' : ErrorCommand,
		'UpdateRequestProxyCommand' : UpdateRequestProxyCommand,
		'UpdateParameterProxyCommand' : UpdateParameterProxyCommand,
		'UpdateTableDataProxyCommand' : UpdateTableDataProxyCommand,
		'UpdateChartedCommand' : UpdateChartedCommand,
		'UpdateChartDataCommand' : UpdateChartDataCommand,
		'ShowLoadMaskCommand' : ShowLoadMaskCommand,
		'HideLoadMaskCommand' : HideLoadMaskCommand,
		'UpdateHashCommand' : UpdateHashCommand,
		'UpdateTopicHashCommand' : UpdateTopicHashCommand,
		'UpdateReportHashCommand' : UpdateReportHashCommand,
		'UpdatePlantHashCommand' : UpdatePlantHashCommand,
		'OpenAPIWindowCommand' : OpenAPIWindowCommand,
		'ResetChartedCommand' : ResetChartedCommand,
		'ResetChartindexedCommand' : ResetChartindexedCommand,
		'ResetStartEndCommand' : ResetStartEndCommand,
		'ResetDatecodeCommand' : ResetDatecodeCommand,
		'ResetAggColumnsCommand' : ResetAggColumnsCommand,
		'OutputToCSVCommand' : OutputToCSVCommand,
		'UpdateParameterFrequencies' : UpdateParameterFrequencies,
		'TableParamsUpdatedCommand' : TableParamsUpdatedCommand,
		'UpdateMineHashCommand' : UpdateMineHashCommand,
		'FrequencySelectionUpdatedCommand' : FrequencySelectionUpdatedCommand,
		'UpdateShipmentHashCommand' : UpdateShipmentHashCommand
		
	};
});
define('js/model/ConfigMixinVisualization',[], function(){
	return {
		getVisualizations : function() {
			return this.getData().visualizations;
		},
		
		setVisualizations : function(visualizations) {
			this.getData().visualizations = visualizations;
		}
	};
});
define('js/model/ConfigMixinDownload',[], function(){
	return {
		getDownloadParameters : function(){
			return this.getData().downloadParameters
		},
		
		setDownloadParameters : function(parameters){
			this.getData().downloadParameters = parameters;
		}
	};
});
	
define('js/model/ConfigMixinViewStack',[], function(){
	return {
		getViews : function() {
			return this.getData().views;
		},
		
		setViews : function(views) {
			this.getData().views = views;
			this.getData().currentView = !(this.getData().currentView) ? views[0] : this.getData().currentView;
		},
		
		getCurrentView : function() {
			return this.getData().currentView;
		},
		
		setCurrentView : function(view) {
			return this.getData().currentView = ($.inArray(view, this.getViews()) == -1 ? this.getViews()[0] : view);
		}
	};
});
define('js/model/ConfigMixinLinkType',[], function(){
	return {
		getLinkTypes : function() {
			return this.getData().linkTypes;
		},
		
		setLinkTypes : function(linkTypes) {
			this.getData().linkTypes = linkTypes;
		}
	};
});
define('js/model/AggregateDataConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/ConfigMixinVisualization',
	'js/model/ConfigMixinDownload',
	'js/model/ConfigMixinViewStack',
	'js/model/ConfigMixinLinkType'
], function(
	ConfigProxy,
	ConfigMixinVisualization,
	ConfigMixinDownload,
	ConfigMixinViewStack,
	ConfigMixinLinkType
){
	return ConfigProxy.$extend({
		__include__ : [
			ConfigMixinVisualization,
			ConfigMixinDownload,
			ConfigMixinViewStack,
			ConfigMixinLinkType
		]
	});
});
define('js/model/RequestMixinTopic',[], function(){
	return {
		getTopicId : function(){
			return this.getData()['0'];
		}
	};
});
define('js/model/AggregateDataRequestProxy',[
	'js/model/RequestProxy',
	'js/model/RequestMixinTopic'
], function(
	RequestProxy,
	RequestMixinTopic
){
	return RequestProxy.$extend({
		__include__ : [
			RequestMixinTopic
		]
	});
});
define('js/model/ParameterMixinAggColumn',[
	'js/model/ConfigProxy'
], function(
	ConfigProxy
){
	return {
		getAgg : function(){
			return this.getQuery()['agg'];
		},
		
		getAggColumnParams : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			currentTopic = configProxy.getTopic(this.getTopicId()),
			ret = {},
			aggColumn;
			
			for(i=0; i < currentTopic['aggColumns'].length; i++){
				aggColumn = currentTopic['aggColumns'][i]['filterGroup']['varName'];
				ret[aggColumn] = this.getQuery()[aggColumn];
			}
			
			return ret;
		},
		
		validateAggColumns : function(def, topic){
			var aggColumns, aggColumn, i;
			var defaults = $.parseJSON(topic.defaultSelection)['query'];
			
			this.checkVal(def, 'agg', defaults['agg'], function(val){
				var aggColumns = (val.length > 0 ? val.split(',') : []),
				ret = ((aggColumns.length > 0) ),
				i,j,found=false;
				for(i=0; i<aggColumns.length; i++) {
					found=false;
					for(j=0; j<topic['aggColumns'].length;j++) {
						if(aggColumns[i] == topic['aggColumns'][j]['aggColumnId']) {
							found=true;
							break;
						}
					}
					ret &= found;
				}
				return ret;
			});
			
			aggColumns = def['agg'].split(',');
			
			for(i=0; i<aggColumns.length; i++){
				aggColumn = topic['aggColumns'][i];
				this.checkVal(def, aggColumn['filterGroup']['varName'], defaults[aggColumn['filterGroup']['varName']], function(val){
					var ret = val.length > 0,
					i, code;
					
					for(i=0; i<val.length; i++){
						code = val.charCodeAt(i);
						ret &= (code >= 48 && code <= 57) || (code >= 97 && code <= 118)
					}
					return ret;
				});
			}
		},
		
		resetAggColumns : function(topicId){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			newTopic = configProxy.getTopic(topicId),
			query = this.getQuery(),
			aggColumnParams = this.getAggColumnParams(),
			newAggColumns = {},
			varName, i;
			
			for(i=0; i< newTopic.aggColumns.length; i++)
				newAggColumns[newTopic.aggColumns[i].filterGroup.varName] = null;
			for(varName in aggColumnParams){
				if(!(varName in newAggColumns))
					delete query[varName];
			}
		}
	};
});
define('js/model/ParameterMixinGetCharted',[], function(){
	return $.extend({
		getCharted : function(type){
			return this.getQuery()[type];
		}
	});
});
define('js/model/ParameterMixinChart',[
	'js/model/ParameterMixinGetCharted',
	'js/model/ConfigProxy'
],function(
	ParameterMixinGetCharted,
	ConfigProxy
){
	return ParameterMixinGetCharted.extend({

		arePinnedSeriesCharted : function(type){
			var chartedIds = this.getChartedIds(type);
			var pinnedIds = this.getChartedIds('pin');
			var pinnedMap = {};

			for(var i=0; i<pinnedIds.length; i++)
				pinnedMap[pinnedIds[i]] = true;
			for(i=0; i<chartedIds.length; i++)
				if(chartedIds[i] in pinnedMap) return true;
			return false;
		},
		
		updateChartIdFrequencies: function(freq) {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var visualizations = [];
			
			if(typeof configProxy.getVisualizations !== 'undefined') {
				visualizations = $.merge([], configProxy.getVisualizations());
			}
			if(typeof configProxy.getLinkTypes !== 'undefined') {
				visualizations = $.merge(visualizations, configProxy.getLinkTypes());
			}
			for(var i=0; i < visualizations.length; i++){
				var klass = visualizations[i]['class'];
				if(klass != 'sourcekey') {
					var seriesIds = this.getChartedIds(klass);
					for(var j=0; j < seriesIds.length; j++) {
						seriesIds[j] = seriesIds[j] != null ? seriesIds[j].substring(0, seriesIds[j].length-1) + freq : null;
					}
					this.updateCharted(seriesIds, klass);
				}
			}
		},
		
		getUpdatedCharted : function(ids, chartType, remove){
			return this.getUpdatedChartedIds(ids, chartType, remove).join('~');
		},
		
		getUpdatedChartedIds : function(ids, chartType, remove){
			var chartedIds = this.getChartedIds(chartType).slice();
			ids = ids instanceof Array ? ids : [ids];
			
			for(var i=0; i<ids.length; i++){
				var id = ids[i];
				var idx = $.inArray(id, chartedIds);
				if(!~idx && remove !== true) {
					var nullIdx = $.inArray(null, chartedIds);
					if(!~nullIdx) {
						chartedIds.push(id);
					}
					else {
						chartedIds[nullIdx] = id;
					}
				}
				else if(~idx && remove !== false) {
					chartedIds[idx] = null;
//					chartedIds.splice(idx, 1);
				}
			}
			
			return chartedIds;
		},
		
		getChartedByType : function(type) {
			return this.getQuery()[type];
		},
		
		getChartedIds : function(chartType){
			var charted = this.getCharted(chartType);
			var ret = charted ? charted.split('~') : [];
			$.each(ret, function(index, value) {
				if(ret[index] == '') {
					ret[index] = null;
				}
			});
			return ret;
		},
		
		getChartIndexed : function(){
			return this.getQuery()['chartindexed'];
		},
		
		getColumnEndpoints : function(){
			return this.getQuery()['columnendpoints'];
		},
		
		getColumnValues : function(){
			return this.getQuery()['columnvalues'];
		},
		
		resetCharted : function(chartType){
			if(chartType !== void 0)
				this.deleteNonPinnedIds(chartType);
			else{
				var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
				var visualizations = configProxy.getVisualizations();
				for(var i=0; i < visualizations.length; i++){
					var klass = visualizations[i]['class'];
					this.deleteNonPinnedIds(klass);
				}
			}
		},

		deleteNonPinnedIds : function(type){
			var query = this.getQuery();
			if(query[type] !== void 0 && query['pin'] !== void 0) {
				var pinnedSeriesIds = {};
				$.each(query['pin'].split('~'), function(idx, id) {
					pinnedSeriesIds[id] = true;
				});

				var newCharted = $.map(query[type].split('~'), function(id) {
					if(id in pinnedSeriesIds) {
						return id;
					}
					else {
						return null;
					}
				}).join('~');

				if(newCharted === '') {
					delete query[type];
				}
				else {
					query[type] = newCharted;
				}
			}
			else {
				delete query[type];
			}
		},
		
		resetChartindexed : function(){
			var query = this.getQuery();
			delete query.chartindexed;
		},
		
		updateCharted : function(chartedIds, chartType){
			var query = this.getQuery();
			chartedIds = chartedIds instanceof Array ? chartedIds : [chartedIds];
			query[chartType] = chartedIds.join('~');
		},
		
		updateChartType : function(type) {
			var query = this.getQuery();
			if(!query[type]) { 
				query[type] = '';
			}
			query['ctype'] = type;
		},
		
		getChartType : function() {
			return this.getQuery()['ctype'];
		},
		
		getMapType : function(){
			return this.getQuery()['maptype'];
		},
		
		validateChartParams : function(def, topic, items) {
			var defaults = $.parseJSON(topic.defaultSelection);
			
			this.checkVal(def, 'maptype', '0', function(val){
				return val === '0' || val === '1';
			});
			this.checkVal(def, 'ctype', defaults['ctype'] ? defaults['ctype'] : items[0]['class'], function(val){
				var ret = false;
				$.each(items, function(index, value) {
					if(value['class'] == val) {
						ret = true;
					}
				});
				return ret;
			});
		},
		
		isUserGenerated : function(ctype) {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var topic = configProxy.getTopic(this.getTopicId());
			var defaults = $.parseJSON(topic.defaultSelection);
			var charted = $.extend(true, [], this.getChartedIds(ctype)).sort();
			var defaultsArr = $.extend(true, [], defaults[ctype]).sort();
			if(defaultsArr.length == charted.length) {
				for(var i = 1; i < defaultsArr.length; i++) {
					if(defaultsArr[i] != charted[i]) {
						return true;
					}
				}
			}
			else {
				return true;
			}
			return this.arePinnedSeriesCharted(ctype) || false;
		}
	});
});
	
define('js/model/ParameterMixinFrequency',[], function(){
	return {
		getFreq : function(){
			return this.getQuery()['freq'];
		},
		
		getFreqTitle : function(){
			var ret = '';
			
			switch(this.getFreq()){
				case 'A':
					return 'Annual';
					break;
				case 'Q':
					return 'Quarterly';
					break;
				case 'M':
					return 'Monthly';
					break;
			}
			
			return ret;
		},
		
		validateFrequency : function(def, topic){
			var defaults = $.parseJSON(topic.defaultSelection);
			
			this.checkVal(def, 'freq', defaults['freq'], function(val){
				switch(val){
					case 'A':
						return topic.hasAnnual;
						break;
					case 'Q':
						return topic.hasQuarterly;
						break;
					case 'M':
						return topic.hasMonthly;
						break;
				}
				return false;
			});
		}
	};
});
define('js/model/ParameterMixinLinkType',[], function(){
	return {
		getLinkType : function() {
			return this.getQuery()['ltype'];
		},
		
		updateLinkType : function(ltype) {
			this.getQuery()['ltype'] = ltype;
		},
		
		validateLinkType : function(def, topic, items) {
			var defaults = $.parseJSON(topic.defaultSelection);
			
			this.checkVal(def, 'ltype', defaults['ltype'] ? defaults['ltype'] : items[0]['class'], function(val){
				var ret = false;
				$.each(items, function(index, value) {
					if(value['class'] == val) {
						ret = true;
					}
				});
				return ret;
			});
		}
	};
});
define('js/model/ParameterMixinReport',[
	'js/model/ConfigProxy'
], function(
	ConfigProxy
){
	return {
		getReportIndex : function(){
			return this.getData()['reportIndex'];
		},
		
		updateReportIndex : function(){	
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var reports = configProxy.getReports();
			
			$.extend(this.getData(), {
				'reportIndex' : null
			});
			
			for(var i = 0; i < reports.length; i++) {
				var params = $.parseJSON(reports[i].json);
				if(this.getTopicId() == params.topic) {
					var match = true;
					for(var key in params.query) {
						if(params.query[key] != this.getQuery()[key]) {
							match = false;
						}
					}
					if(match) {
						$.extend(this.getData(), {
							'reportIndex' : i
						});
						return;
					}
				}
			}
		}
	};
});
define('js/model/ParameterMixinRSE',[], function(){
	return {
		getShowRSE : function(){
			return this.getQuery()['rse'] === '1';
		},
		
		validateShowRSE : function(def, topic){
			this.checkVal(def, 'rse', '0', function(val){
				return val === '0' || val === '1';
			});
		}
	};
});
define('js/model/ParameterMixinStartEnd',[], function(){
	return {
		getStart : function(){
			return this.getQuery()['start'];
		},
		
		getEnd : function(){
			return this.getQuery()['end'];
		},
		
		resetStartEnd : function(){
			var query = this.getQuery();
			delete query.start;
			delete query.end;
		},
		
		validateStartEnd : function(def, topic){
			var startCode, endCode
			switch(def['freq']){
				case 'A':
					startCode = topic['annualStart'];
					endCode = topic['annualEnd'];
					break;
				case 'Q':
					startCode = topic['quarterlyStart'];
					endCode = topic['quarterlyEnd'];
					break;
				case 'M':
					startCode = topic['monthlyStart'];
					endCode = topic['monthlyEnd'];
					break;
			}
			
			this.checkVal(def, 'start', startCode, function(val){
				return val >= startCode && val < endCode;
			});
			def['start'] = parseInt(def['start']);
			
			this.checkVal(def, 'end', endCode, function(val){
				return val > def['start'] && val <= endCode;
			});
			def['end'] = parseInt(def['end']);
		}
	};
});

define('js/model/ParameterMixinTopic',[], function(){
	return {
		getTopicId : function(){
			return this.getData()['topicId']
		},
		
		updateTopicId : function(topicId){
			$.extend(this.getData(), {
				'topicId' : topicId
			});
		},
		
		getFullTopicPath : function(params, topicId){
			return 'topic/' +
			(topicId !== void 0 ? topicId : this.getTopicId()) +
			this.getQueryString(params);
		}
	};
});
define('js/model/ParameterMixinStateBasinType',[], function(){
	return {
		getRegionType : function(){
			return this.getQuery()['rtype'];
		},
		
		validateRegionType : function(def, topic){
			var defaults = $.parseJSON(topic.defaultSelection);
			var hasStateBasinFilterGroup = this.hasStateBasinFilterGroup(topic);
			
			this.checkVal(def, 'rtype', 's', function(val){
				switch(val){
					case 's':
						return true;
					case 'b':
						if(hasStateBasinFilterGroup) {
							return true;
						}
						else {
							return false;
						}
						break;
				}
				return false;
			});
		},
		
		hasStateBasinFilterGroup : function(topic) {
			for(var i = 0; i < topic.aggColumns.length; i++) {
				if(topic.aggColumns[i].filterGroup.filterGroupId == 13) {
					return true;
				}
			}
			
			return false;
		}
	};
});
define('js/model/AggregateDataParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/RequestProxy',
	'js/model/ParameterMixinAggColumn',
	'js/model/ParameterMixinChart',
	'js/model/ParameterMixinFrequency',
	'js/model/ParameterMixinLinkType',
	'js/model/ParameterMixinReport',
	'js/model/ParameterMixinRSE',
	'js/model/ParameterMixinStartEnd',
	'js/model/ParameterMixinTopic',
	'js/model/ParameterMixinStateBasinType'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	RequestProxy,
	ParameterMixinAggColumn,
	ParameterMixinChart,
	ParameterMixinFrequency,
	ParameterMixinLinkType,
	ParameterMixinReport,
	ParameterMixinRSE,
	ParameterMixinStartEnd,
	ParameterMixinTopic,
	ParameterMixinStateBasinType
){
	return ParameterProxy.$extend({
		
		__include__ : [
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinAggColumn,
			ParameterMixinStartEnd,
			ParameterMixinChart,
			ParameterMixinRSE,
			ParameterMixinLinkType,
			ParameterMixinStateBasinType
		],
		
		getQueryKeys : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId()),
			ret = ['agg'],
			i;
			
			for(i=0; i<topic.aggColumns.length; i++)
				ret.push(topic.aggColumns[i]['filterGroup']['varName']);
				
			$.each(configProxy.getVisualizations(), function(index, value) {
				ret.push(value['class']);
			});
			return ret.concat([
				'freq',
				'start',
				'end',
				'chartindexed',
				'ctype',
				'ltype',
				'columnendpoints',
				'columnvalues',
				'rtype'
			]);
		},
		
		getFullPath : function(params, topicId){
			return this.getFullTopicPath(params, topicId);
		},
		
		getTableParamsString : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId()),
			ret = 'topic=' + this.getTopicId() +
				'&agg=' + this.getAgg(),
			aggColumn;
				
			for(i=0; i<topic.aggColumns.length; i++){
				aggColumn = topic['aggColumns'][i]['filterGroup']['varName'];
				if(typeof this.getQuery()[aggColumn] != 'undefined') {
					ret += '&' + aggColumn + '=' + this.getQuery()[aggColumn];
				}
			}
			
			ret += '&ids=' + this.getCharted('pin');
			
			ret += '&freq=' + this.getFreq();
			
			ret += '&rtype=' + this.getRegionType();
			
			return ret;
		},
		
		getTableParams : function(){
			return $.extend({
				'topic' : this.getTopicId(),
				'freq' : this.getFreq(),
				'agg' : this.getAgg(),
				'ids' : this.getCharted('pin'),
				'rtype' : this.getRegionType()
			}, this.getAggColumnParams());
		},
		
		getFormParams : function(){
			return $.extend({
				'topic' : this.getTopicId(),
				'agg' : this.getAgg()
			}, this.getAggColumnParams());
		},
		
		getChartParams : function(){
			var self = this;
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var params = {
				'chartindexed' : this.getChartIndexed(),
				'ctype' : this.getChartType(),
				'columnendpoints' : this.getColumnEndpoints(),
				'columnvalues' : this.getColumnValues()
			};
			$.each(configProxy.getVisualizations(), function(index, value) {
				params[value['class']] = self.getChartedByType(value['class']);
			});
			
			return $.extend(params, this.getTimeParams());
		},
		
		getTimeParams : function(){
			return {
				'start' : this.getStart(),
				'end' : this.getEnd(),
				'freq' : this.getFreq()
			};
		},
		
		getGAString : function(params, topicId) {
			var qry = $.extend({}, this.getQuery(), params);
			var ret = 'topic/' + (topicId ? topicId : this.getTopicId()) + '/?' + (qry['agg'] ? 'agg=' + qry['agg'] : '') + (qry['freq'] ? '&freq=' + qry['freq'] : '');
			
			$.each(this.getAggColumnParams(), function(key, value) {
				ret += '&' + key + '=' + qry[key];
			});
			
			return ret;
		},
		
		getGAStringByTopic : function(params, topicId) {
			return this.getGAString(params, topicId);
		},
		
		update : function(){
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topicId = requestProxy.getTopicId(),
			tmpTopic = configProxy.getTopic(topicId),
			tableParamsUpdated = topicId !== this.getTopicId(),
			timeParamsUpdated = false,
			chartParamsUpdated =  !('ctype' in requestProxy.getQuery()),
			ctypeParamUpdated = !('ctype' in requestProxy.getQuery()),
			mapTypeParamUpdated = false,
			rseParamUpdated = false,
			topicParamsUpdated = false,
			ltypeParamUpdated = false,
			rtypeParamUpdated = !('rtype' in requestProxy.getQuery()),
			self = this;
			
			topicParamsUpdated |= topicId != this.getTopicId();
			
			this.updateTopicId(topicId);
			
			this.updateQuery(requestProxy.getQuery(), function(tmpKey, newVal, oldVal){
				var i, isAggColumn;
				// Check and see if our time parameters have changed
				timeParamsUpdated |= tmpKey == 'start' || tmpKey == 'end' || tmpKey == 'freq';
				// Check and see if our frequency has changed
				tableParamsUpdated |= tmpKey == 'freq';
				// Check and see if our agg column order has changed
				tableParamsUpdated |= tmpKey == 'agg';
				// Check and see if the charted parameter has changed
				$.each(configProxy.getVisualizations(), function(index, value) {
					chartParamsUpdated |= tmpKey == value['class'];
				});
				tableParamsUpdated |= tmpKey == 'pin';
				// Check and see if the ctype parameter has changed
				chartParamsUpdated |= ctypeParamUpdated |= tmpKey == 'ctype';
				// Check and see if the chartIndexed parameter has changed
				chartParamsUpdated |= tmpKey == 'chartindexed';
				// Check and see if the columnEndpoints parameter has changed
				chartParamsUpdated |= tmpKey == 'columnendpoints';
				// Check and see if the columnValues parameter has changed
				chartParamsUpdated |= tmpKey == 'columnvalues';
				// Check and see if the map type has been changed
				mapTypeParamUpdated |= tmpKey == 'maptype';
				// Check and see if the region type has been changed
				rtypeParamUpdated |= tmpKey == 'rtype';
				// Check and see if the parameter is a relavent agg column
				for(i=0; i < tmpTopic['aggColumns'].length && !tableParamsUpdated; i++){
					isAggColumn = tmpKey == tmpTopic['aggColumns'][i]['filterGroup']['varName'];
					tableParamsUpdated |= isAggColumn;
				}
				// Check adn see if the rse parameter has changed
				rseParamUpdated |= tmpKey == 'rse';
				
				//Check and see if the Link Type parameter has changed
				ltypeParamUpdated |= tmpKey == 'ltype';
			});
			
			this.updateReportIndex();
			
			this.updateChartIdFrequencies(this.getFreq());
			
			if(topicParamsUpdated)
				this.sendNotification(Facade.TOPIC_PARAMS_UPDATED);
			if(tableParamsUpdated)
				this.sendNotification(Facade.TABLE_PARAMS_UPDATED);
			if(ctypeParamUpdated){
				if(this.getChartType() != 'pin' && this.getChartType() != 'sourcekey')
					this.sendNotification(Facade.UPDATE_VISUALIZATION_VIEW_STACK);
				this.sendNotification(Facade.CTYPE_PARAM_UPDATED);
			}
			if(mapTypeParamUpdated)
				this.sendNotification(Facade.MAP_TYPE_PARAM_UPDATED);
			if(chartParamsUpdated)
				this.sendNotification(Facade.CHART_PARAMS_UPDATED);
			if(timeParamsUpdated)
				this.sendNotification(Facade.TIME_PARAMS_UPDATED);
			if(rseParamUpdated)
				this.sendNotification(Facade.RSE_PARAM_UPDATED);
			if(ltypeParamUpdated)
				this.sendNotification(Facade.PIN_KEY_PARAM_UPDATED);
			if(rtypeParamUpdated)
				this.sendNotification(Facade.REGION_TYPE_PARAM_UPDATED);
		},
		
		validateQuery : function(vals){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId());
			
			this.validateFrequency(vals, topic);
			this.validateAggColumns(vals, topic);
			this.validateStartEnd(vals, topic);
			this.validateChartParams(vals, topic, configProxy.getVisualizations());
			this.validateShowRSE(vals, topic);
			this.validateLinkType(vals, topic, configProxy.getLinkTypes());
			this.validateRegionType(vals, topic);
		}
		
	});
});
define('js/model/TableDataMixinAnalysis',[], function(){
	return {
		getRelatedAnalysis : function(){
			return this.getData()['ANALYSIS']['ARTICLES'];
		},
		getMoreAnalysisURL : function(){
			return this.getData()['ANALYSIS']['MORE_URL'];
		}
	};
});
	
define('js/model/TableDataMixinChart',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy
){
	return {
		getIDMap : function(){
			return this.getData()['IDMAP'];
		},
		
		getChartData : function(type){
			if(type !== void 0)
				return this.getData()['chartData'][type];
			else
				return this.getData()['chartData'];
		},
		
		updateChartData : function(){ 
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var chartType = parameterProxy.getChartType();
			var updatedIndices = this.processChartData(chartType, true);
			if(chartType != 'pin')
				this.sendNotification(Facade.CHART_DATA_UPDATED, updatedIndices);
			else
				this.updatePinnedSeries();
		},
		
		createIDMap : function(){
			var combinedData = this.getCombinedData();
			var idMap = {};
			for(var i=0; i < combinedData.length; i++){
				var id = combinedData[i].SERIES_ID;
				if(!idMap[id])
					idMap[id] = [];
				idMap[id].push(i);
			}
			this.getData().IDMAP = idMap;

			return idMap;
		},
		
		updatePinnedSeries : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var visualizations = configProxy.getVisualizations();
			var tableParamsString = parameterProxy.getTableParamsString();
			var chartData = this.getChartData();
			var otherVisData = {};
			var pinnedData = chartData['pin'];
			var seriesData = this.getSeriesData().slice();
			var startingPinnedIndex = seriesData.length - 1;
			var idMap = this.getIDMap();
			var pinnedMap = {};
			var seriesMap = {};
			var visChanged = false;
			var updatedIndices, klass, i, j;

			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
			}
			else{
				for(i=0; i < seriesData.length; i++){
					var datum = seriesData[i];
					seriesMap[datum.SERIES_ID] = true;
				}
				
				for(i=0; i < pinnedData.length; i++){
					var datum = pinnedData[i];
					if(!(datum.SERIES_ID in seriesMap)){
						seriesData.push($.extend({PINNED:true}, datum, {LEVEL:0}));
					}
					pinnedMap[datum.SERIES_ID] = true;
				}
				
				for(i=0; i < seriesData.length; i++){
					var datum = seriesData[i];
					if(!(datum.SERIES_ID in pinnedMap)){
						seriesData.splice(i, 1);
					}
				}

				this.setData(tableParamsString, $.extend({}, this.getData(), {
					SERIESDATA : {
						DATA : seriesData
					}
				}));
			}
			
			var idMap = this.createIDMap();
			

			for(i=0; i < visualizations.length; i++){
				visChanged = false;
				klass = visualizations[i]["class"];
				if(klass !== 'pin' && klass != 'sourcekey'){
					var vis = chartData[klass];
					for(j=0; j < vis.length; j++){
						datum = vis[j];
						if(!(datum.SERIES_ID in idMap)){
							vis.splice(j, 1);
							var visChanged = true;
						}
					}
					if(visChanged){
						this.sendNotification(
							Facade.DEFAULT_CHART_SET, 
							$.map(vis, function(datum){
								return datum.SERIES_ID
							}),
							klass
						);
						this.processChartData(klass);
					}
				}
			}

			this.sendNotification(Facade.TABLEDATA_UPDATED);
		},
		
		processChartData : function(chartType,allowNoSeries){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var updatedIndices = [];
			var newChartData = [];
			var combinedData = this.getCombinedData();
			var tableData = this.getTableData();
			var idMap = this.getIDMap();
			var i, j, k, index, datum, chartIndicesInvalid, children, allChildrenChecked;
			
			var chartTypes = chartType !== void 0 ? [chartType] : 
				$.map(configProxy.getVisualizations(), function(vis){
					return vis["class"];
				});

			for(var k = 0; k < chartTypes.length; k++){
				newChartData = [];
				chartType = chartTypes[k];
				var chartedIds = parameterProxy.getChartedIds(chartType);
				var chartedIdsCopy = chartedIds.slice();
				var oldChartData = this.getChartData(chartType) || [];

				if(chartedIdsCopy.length > 0){
					for(i = 0; i < chartedIdsCopy.length; i++){
						var idAdded = false;
						var indices;
						
						if(chartType == 'map') {
							indices = this.getMappedSeries(chartedIdsCopy[i], idMap);
						}
						else {
							indices = idMap[chartedIdsCopy[i]];
						}
						if(indices){
							for(j=0; j < indices.length; j++){
								datum = combinedData[indices[j]];
								if(datum.HAS_DATA){
									datum[chartType] = true;
									updatedIndices.push(indices[j]);
									if(!idAdded){
										idAdded = true;
										newChartData.push(datum);
										var oldIndex = $.inArray(datum, oldChartData);
										if(oldIndex >= 0) {
											oldChartData.splice(oldIndex, 1);
										}
									}
								}
							}
						}
						else if(chartedIdsCopy[i] == null) {
							newChartData.push(null);
						}
						else{
							chartedIds.splice(i, 1);
						}
					}
				}
				
				if(chartedIds.length < 1 && chartType != 'pin' && chartType != 'sourcekey'){
					if(!allowNoSeries || chartType == 'map') {
						var topic = configProxy.getTopic(parameterProxy.getTopicId());
						var query = parameterProxy.getQuery();
						var defaults = $.parseJSON(topic.defaultSelection);
						var matchesDefault = true;
						
						$.each(defaults['query'], function(key, value) {
								if(query[key] != value) {
									matchesDefault = false;
								}
						});
						if(matchesDefault && defaults[chartType]) {
							for(j=0; j< defaults[chartType].length; j++) {
								for(i=0; i< tableData.length; i++){
									datum = tableData[i];
									if(datum.SERIES_ID.substr(0, datum.SERIES_ID.length-1) == defaults[chartType][j].substr(0, defaults[chartType][j].length-1)) {
										datum[chartType] = true;
										newChartData.push(datum);
										chartedIds.push(datum.SERIES_ID);
										updatedIndices = updatedIndices.concat(idMap[datum.SERIES_ID]);
										break;
									}
								}
							}
						}
						else {
							for(i=0, j=0; i < tableData.length && j < 1; j++){
								datum = null;
								for(i; i < tableData.length && (!datum || !datum['HAS_DATA']); datum = tableData[i++]);
									datum[chartType] = true;
									newChartData.push(datum);
									chartedIds.push(datum.SERIES_ID);
									updatedIndices = updatedIndices.concat(idMap[datum.SERIES_ID]);
							}
						}
						if(chartType == 'map' && chartedIds.length == 1) {
							var indices = this.getMappedSeries(chartedIds[0], idMap);
							$.each(indices, function(index, value) {
								var datum = combinedData[value];
								datum[chartType] = true;
							});
						}
					}
					this.sendNotification(Facade.DEFAULT_CHART_SET, chartedIds, chartType);
				}
				
				for(i=0; i<oldChartData.length; i++){
					datum = oldChartData[i];
					if(datum != null && !~$.inArray(datum.SERIES_ID, chartedIds)){
						var indices = [];
						
						if(chartType == 'map') {
							indices = this.getMappedSeries(datum.SERIES_ID, idMap);
						}
						else {
							indices = idMap[datum.SERIES_ID];
						}
						
						for(var j=0; j < indices.length; j++){
							datum = combinedData[indices[j]];
							if(datum.HAS_DATA){
								if(chartType != 'pin' || !datum.PINNED) datum[chartType] = false;
								updatedIndices.push(indices[j]);
							}
						}
					}
				}
				
				for(i=0; i<combinedData.length; i++){
					datum = combinedData[i];
					if(!datum['HAS_DATA'] && datum['LEVEL_GROUPS']){
						children = this.getChildData(i);
						allChildrenChecked = true && (children.length > 0);
						for(j=0; j < children.length; j++){
							allChildrenChecked &= children[j][chartType];
						}
						if(datum[chartType] != allChildrenChecked)
							updatedIndices.push(i);
						datum[chartType] = allChildrenChecked;
					}
				}
				
				this.getData()['chartData'][chartType] = newChartData;
			}
			return updatedIndices;
		},

		getMappedSeries : function(seriesId, idMap) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var topics = configProxy.getTopics(), topic;
			var regex;

			for(var i = 0; i < topics.length; i++){
				regex = new RegExp(topics[i].data_set + '\\.' + topics[i].dataType + '\\..*');
				if(seriesId.match(regex)){
					topic = topics[i];
					break;
				}
			}

			var ret = [];
			
			var seriesIdArr = seriesId.split('.');
			var filterValArr = seriesIdArr[2].split('-');
			
			$.each(topic.aggColumns, function(index, value) {
				if(value.filterGroup.varName == 'geo') {
					filterValArr[index] = '[A-Za-z]+';
					return false;
				}
			});
			
			seriesIdArr[2] = filterValArr.join('\\-');
			
			regex = new RegExp(seriesIdArr.join('\\.'));
			$.each(idMap, function(key, value) {
				if(key.match(regex)) {
					ret = ret.concat(value);
				}
			});
			
			return ret;
		}
	};
});
	
define('js/model/TableDataMixinLevel',[], function(){
	return {
		processLevels : function(){
			var tableData = this.getTableData(),
			levelGroups = [],
			levelGroupCounters = [],
			i, j, k, level, datum;
			
			for(i=tableData.length - 1; i>=0; i--){
				datum = tableData[i];
				datum['LEVEL_GROUPS'] = [];
				level = datum['LEVEL'];
				// Reset all of the groups after the current level
				for(j=levelGroups.slice(0).length - 1; j > level; j--){
					levelGroups.pop();
				}
				for(k=level; k>0; k--){
					if(levelGroups[k] === void 0){
						if(k == level){
							datum['IS_LAST'] = true;
							datum['LEVEL_GROUPS'][k] = levelGroups[k] =
							levelGroupCounters[k] !== void 0 ?
							++levelGroupCounters[k] : levelGroupCounters[k] = 0;
						}
					}
					else
						datum['LEVEL_GROUPS'][k] = levelGroups[k];
				}
			}
		}
	};
});
define('js/model/AggregateDataTableDataProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/model/TableDataMixinAnalysis',
	'js/model/TableDataMixinChart',
	'js/model/TableDataMixinLevel'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy,
	TableDataMixinAnalysis,
	TableDataMixinChart,
	TableDataMixinLevel
){
	return TableDataProxy.$extend({
		
		isUpdating : false,
		
		__include__ : [
			TableDataMixinLevel,
			TableDataMixinChart,
			TableDataMixinAnalysis
		],
		
		getDataColumns : function(){
			return this.getData()['TABLEDATA']['DATACOLUMNS'];
		},
		
		getMinColumn : function(){
			return this.getDataColumns[0];
		},
		
		getMaxColumn : function(){
			return this.getDataColumns()[this.getDataColumns().length - 1];
		},
		
		getTableData : function(){
			return this.getData()['TABLEDATA']['ROWS'];
		},
		
		getSeriesData : function(){
			return this.getData()['SERIESDATA']['DATA'];
		},
		
		getCombinedData : function(){
			var ret = [];
			var seriesData = this.getSeriesData();
			var tableTitle = this.getTableTitle();
			var tableData = this.getTableData();
			var tableUnits = this.getTableUnits();
			
			if(seriesData.length > 0)
				ret.push({
					DESCRIPTION: 'Pinned Series',
					ACTION : this.$class.CLEAR
				});
			
			ret = ret.concat(seriesData);
			
			var titleData = {
				DESCRIPTION : tableTitle,
				SHOW_UNITS : tableUnits !== null,
				UNITS : tableUnits
			};
			
			if(ret.length == 0) {
				titleData.ACTION = this.$class.CLEAR;
			}
			
			ret.push(titleData);
			
			ret = ret.concat(tableData);
			
			return ret;
		},
		
		getTableUnits : function(){
			return this.getData()['TABLEDATA']['UNITS'] === '' ? null : this.getData()['TABLEDATA']['UNITS'];
		},
		
		getTableTitle : function(){
			return this.getData()['TABLEDATA']['DESCRIPTION'];
		},
		
		getChildRows : function(row) {
			var ret = [];
			var combinedData = this.getCombinedData();
			for(var i = row+1; i < combinedData.length && combinedData[i].LEVEL > combinedData[row].LEVEL; i++) { 
				if(combinedData[i].HAS_DATA) ret.push(i);
			}
			
			return ret;
		},
		
		getChildData : function(row){
			var childRows = this.getChildRows(row);
			var combinedData = this.getCombinedData();
			var ret = [];
			for(i=0; i < childRows.length; i++){
				ret.push(combinedData[childRows[i]]);
			}
			return ret;
		},
		
		loadTableData : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableParamsString = parameterProxy.getTableParamsString(),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			self = this;
			
			this.isUpdating = true;
			
			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
				this.processChartData();
				this.createIDMap();
				this.isUpdating = false;
				this.sendNotification(Facade.TABLEDATA_UPDATED);
			}
			else{
				$.ajax(
					encodeURI(
						this.$class.PROXY_URL + '?method=' +
						this.$class.PROXY_METHOD + '&' + tableParamsString
					),
					{
						dataType : 'json',
						error : function(){
							self.sendNotification(Facade.ERROR);
						},
						success : function(data){
							$.each(data.TABLEDATA.ROWS.concat(data.SERIESDATA.DATA), function() {
								this.filterQuery = configProxy.getFilterQuery(parameterProxy.getTopicId(), this.SERIES_ID);
								this.agg = parameterProxy.getAgg();
							});
							// Wrap the data array in our TableData class
							data.TABLEDATA.ROWS = $.extend(data.TABLEDATA.ROWS, {
								'getRowMetadata' : self.$class.getRowMetadata
							});
							data.chartData = {};
							self.setData(tableParamsString, data);
							self.createIDMap();
							self.processLevels();
							self.processChartData('linechart');
							self.processChartData('columnchart');
							self.processChartData('map');
							self.processChartData('pin');
							self.isUpdating = false;
							self.sendNotification(Facade.TABLEDATA_UPDATED);
						}
					}
				);
			}
		},
		
		__classvars__ : {
			PROXY_URL : 'data/index.php',
			PROXY_METHOD : 'getAggregateData',
			CLEAR : 'clear'
		}
	});
});
define('js/view/HelpTooltipsCDB',[], function(){
	return function(){
		return [
			//Help button
			{selector: '#help_button', tip:  '<iframe width="230" height="200" src="http://www.youtube.com/embed/SloSlzl4_hk?list=UU7VrRTLQhc79Zie7Oehl0Vg" frameborder="0" allowfullscreen=""></iframe>Play the short video to learn how to use this tool.  Click anywhere else to close these help tips.', position: 'topMiddle'},
			
			//Top topic/report selection
			{selector: '#top_topic_selection_container', tip:  'This is the top level selector for the <b>Coal Data Browser</b>.  Choose a report to automatically select a dataset and configure the filter.  Alternatively, you can select a dataset and configure the filter directly.  It may be helpful to use the reports as a starting point and then modify them using the FILTER/ORDER control below.', position: 'leftMiddle'},
			
			//Middle topic/report selection
			{selector: '#middle_topic_selection_container', tip:  'This is the top level selector for the <b>Coal Data Browser</b>.  Choose a report to automatically select a dataset and configure the filter.  Alternatively, you can select a dataset dand configure the filter directly.  It may be helpful to use the reports as a starting point and then modify them using the FILTER/ORDER control below.', position: 'leftTop', yAdjust:15},
			
			//Chart icons
			{selector: '.slick-pane-left .slick-row:nth-child(1) .chart_column:nth-child(3)', tip:  'Click on the<span class="ico linechart selected"></span><span class="ico columnchart selected"></span><span class="ico map selected"></span> icons to add and remove series from the results table to the graph. Use the toggle buttons above the table to change the action.', position: 'rightMiddle', xAdjust: 0, yAdjust:178},
			
			//Pin Key Column Chart icons
			{selector: '.slick-pane-left .slick-row:nth-child(1) .chart_column:nth-child(1)', tip:  'Click the <span class="ico pin selected"></span> icon to "pin" the series for comparison across data sets. Click the<span class="ico sourcekey selected"></span> icon to open a link to the series in EIA\'s API. Use the toggle buttons above the table to change the action.', position: 'leftMiddle', xAdjust: 0, yAdjust:178},
			
			//Download Button
			{selector: '#main_download_button', tip:  'This menu provides options to download the table or graphic data in <span class="ico_csv">CSV</span> format, as well as an <span class="ico_img ico_pdf">image</span> or <span class="ico_pdf">PDF</span> of the graphic.', position: 'rightMiddle'},
			
			//Filter/order Button
			{selector: '#main_filter_button', tip:  'Once you have choosen a dataset, open the FILTER/ORDER tool.  Use the left and right arrows to select the prime filter.  The prime filter only allows a single selection, creating manageable results set.  You will find that you can quickly get, visualize and download the coal information you need by carefully choosing the category order and the filters.', position: 'leftBottom', yAdjust: -15},
			
			//Visualization Button
			{selector: '#main_visualization_button', tip: 'Use this menu to change the action associated with the icons in the table. The<span class="ico linechart selected"></span>time-series chart,<span class="ico columnchart selected"></span>column chart, and<span class="ico map selected"></span>map selections change the type of graphic displayed above.', position: 'bottomRight', xAdjust: -100},
			
			//Pin/API Key Button
			{selector: '#pinkey_container', tip: 'Use this menu to change the action associated with the icons in the table. The<span class="ico sourcekey selected"></span>API icon provides a link to EIA\'s API for each series. The<span class="ico pin selected"></span>pin series icon allows you to preserve a series for comparison across data sets.', position: 'bottomRight'},
		
			//Analysis Button
			{selector: '#main_analysis_button', tip: 'Click here to display analysis and links related to the current data being shown.', position: 'topMiddle'},
			
			//Terms and Definitions
			{selector: '#termsLink', tip: 'Click this link to see any notes and definitions of terms.', position: 'topMiddle'},
			
			//Map Controls 
			{selector: '.animated_map_button_container', tip: 'Use this interface to play the map over time or scroll to a specific period.', position: 'rightMiddle'},
			
			//Values/Grown toggler
			{selector: '#animated_map_type_container', tip: 'Use these buttons to toggle between coloring based on the current period value or the percent change from the previous period.', position: 'rightMiddle'},
			
			//Chart Indexing Controls
			{selector: '.chartIndexing:visible', tip: 'These controls change the charting options. For time-series charts, you may index the chart to the first point by value or percent. For column charts, you may change between values and percent change, and change which data points are graphed.', position: 'rightTop', yAdjust: 20},
			
			//Data Click through links
			{selector: '.slick-viewport.slick-viewport-top.slick-viewport-right:has(.link_cell)', tip:'When possible, click the hyperlinks on each data point to view to the coal mines and plants that comprise that data point.', position:'center', yAdjust:100},
			
			//Plant List click through links
			{selector: '.slick-viewport.slick-viewport-top.slick-viewport-left:has(.link_cell)', tip:'Click these links to see data for a particular mine or plant.', position:'rightMiddle', yAdjust:-200, xAdjust: -800},
			
			//Table Hierarchy explanation
			{selector: '.slick-viewport.slick-viewport-top.slick-viewport-left:has(.description.aggregate)', tip:'The categories displayed and the hierarchy can be change via the FILTER/ORDER control to the upper left of this table.', position:'center', yAdjust:-100},
			
			//Aggregate Level Map Click through
			{selector: '.aggregate .clickable-regions #mapContainer', tip:'When possible, click an individual state to view to the coal mines and plants that comprise the data point for that state.', position:'center'},
			
			//PlantList Level Map Region Click
			{selector: '.plantList #mapContainer', tip:'When possible, click an individual state to see a map of that state with location markers for the queried power plants. Click on a power plant marker to see the data for it.', position:'center'},

			//State/Basin Toggler
			{selector: '#region_type_selection_container', tip:'Use these buttons to toggle between viewing data sorted by state or by coal basin.', position:'topMiddle'},
			
			//Mine/Plant Toggler
			{selector: '#mine_plant_button', tip:'Use these buttons to toggle between viewing data and locations for coal mines or for coal plants.', position:'topMiddle'},

			//State Map with Clickable Markers
			{selector: '#mapContainer.clickable-markers', tip:'Click on a marker to view the coal shipments to/from that mine or plant.', position:'center'},

			//Import/Export Quantity/Price Togglers
			{selector: '#import_export_button', tip:'Use these buttons to toggle between viewing data on US coal Imports or Exports and between viewing coal Quantity or Price.', position:'leftBottom'},

			//Import/Export US Map
			{selector: '#map_us', tip:'Click a marker to view data associated with that customs district.', position:'center'},

			//Import/Export World Map
			{selector: '#mapContainer.clickable-regions', tip:'Click a region to view imports or exports data associated with that country.', position:'center'}
		]
	};
});


define('js/model/HelpTooltipProxy',[
	'puremvc',
	'js/view/HelpTooltipsCDB'
], 
function(
	puremvc,
	HelpTooltips
){
	return puremvc.Proxy.$extend({
		
		__init__ : function(){
			this.$super(this.$class.NAME, HelpTooltips);
		},
		
		getHelpTooltips : function(){
			return (this.getData())();
		},
		
		__classvars__ : {
			NAME : 'HelpTooltipProxy'
		}
	});
});
define('js/model/MapDataProxy',[
	'js/Facade',
	'js/model/DataProxy',
	'js/model/ParameterProxy',
	'js/model/ConfigProxy'
], function(
	Facade,
	DataProxy,
	ParameterProxy,
	ConfigProxy
){
	return DataProxy.$extend({
		__init__ : function(){
			this.hasData = false;
			this.$super(this.$class.NAME);
		},
		
		updateMapData : function(datum){
			var self = this;
			if(datum && typeof datum != 'undefined') {
				var geosetId = datum.GEOSET_ID,
				title = '',
				units = datum.UNITS,
				precision = datum.PRECISION,
				parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
				configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
				freq = parameterProxy.getFreq(),
				topicId = parameterProxy.getTopicId(),
				cacheId = geosetId + '_' + freq;
				
				if(geosetId){
					if(cacheId in this.cache){
						this.data = this.cache[cacheId];
						this.hasData = true;
						this.sendNotification(Facade.MAP_DATA_UPDATED);
					}
					else{
						$.getJSON('data/index.php',
							{
								'method' : 'getMapData',
								'geosetId' : geosetId,
								'freq' : freq,
								'topic' : topicId
							},
							function(data){
								title = data.TITLE;
								for(var key in data.DATA){
									if(data.DATA.hasOwnProperty(key)){
										var count = data.DATA[key];
										for(var state in count){
											if(count.hasOwnProperty(state)){
												if(isNaN(count[state]))
													delete count[state];
											}
										}
									}
								}
								if(typeof parameterProxy.hasStateBasinFilterGroup == 'function'){
									if(parameterProxy.hasStateBasinFilterGroup(configProxy.getTopic(topicId))) {
										for(var key in data.BASIN_DATA){
											if(data.BASIN_DATA.hasOwnProperty(key)){
												var count = data.BASIN_DATA[key];
												for(var state in count){
													if(count.hasOwnProperty(state)){
														if(isNaN(count[state]))
															delete count[state];
													}
												}
											}
										}
									}
								}
								self.data = self.cache[cacheId] = $.extend(data, {
									TITLE : title,
									UNITS : units,
									PRECISION : precision
								});
								self.hasData = true;
								self.sendNotification(Facade.MAP_DATA_UPDATED);
							}
						);
					}
				}
			}
		},
		
		__classvars__ : {		
			NAME : 'MapDataProxy'
		}
	});
});
	

define('text!templates/footnotes/shipments.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following EIA survey source: Form EIA-923, "Power Plant Operations Report" and the U.S. Department of Labor, Mine Safety and Health Administration, Form 7000-2, "Quarterly Mine Employment and Coal Production Report."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) are typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013. </p>\r\n<p>For more information concerning EIA\'s quarterly data collection and estimation, see the individual footnotes in the Quarterly Coal Report. </p>\r\n';});


define('text!templates/footnotes/reserves_capacity.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following EIA survey source: Form EIA-7A, "Coal Production and Preparation Report."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) is typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013.</p>\r\n<p>For more information concerning EIA\'s annual coal data collection and estimation, see the individual footnotes in the Annual Coal Report. </p>\r\n';});


define('text!templates/footnotes/market_sales.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following EIA survey source: Form EIA-7A, "Coal Production and Preparation Report."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) are typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013. </p>\r\n<p>For more information concerning EIA\'s annual coal data collection and estimation, see the individual footnotes in the Annual Coal Report.</p>\r\n';});


define('text!templates/footnotes/import_export.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following sources: Exports: U.S. Department of Commerce, Bureau of the Census, "Monthly Report 545;" and Imports: U.S. Department of Commerce, Bureau of the Census, "Monthly Report IM 145."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) are typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013. </p>\r\n<p>For more information concerning EIA\'s quarterly data collection and estimation, see the individual footnotes in the Quarterly Coal Report.</p>\r\n';});


define('text!templates/footnotes/consumption.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following EIA survey sources: Form EIA-3, "Quarterly Coal Consumption and Quality Report Manufacturing and Transformation/Processing Coal Plants and Commercial and Institutional Coal Users," Form EIA-5, "Quarterly Coal Consumption and Quality Report Coke Plants," Form EIA-923, "Power Plant Operations Report," and Form EIA-7A, "Coal Production and Preparation Report."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) are typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013. </p>\r\n<p>For more information concerning EIA\'s quarterly data collection and estimation, see the individual footnotes in the Quarterly Coal Report. </p>\r\n';});


define('text!templates/footnotes/mine_production.html',[],function () { return '<p>The U.S. coal data are collected and prepared for release by the Office of Oil, Gas, and Coal Supply Statistics, U.S. Energy Information Administration (EIA). The data are compiled from the following EIA survey source: Form EIA-7A, "Coal Production and Preparation Report" and the U.S. Department of Labor, Mine Safety and Health Administration, Form 7000-2, "Quarterly Mine Employment and Coal Production Report."</p>\r\n<p>Note that data for the most current time periods typically represent preliminary estimates based on samples collected by the surveys. After the end of a calendar year, the estimates are replaced by actual values from a final data collection, except in the case of missing values. The number of missing values (non-responses) is typically minimal. Normally, all data are final by the fall following the data collection year. For example, data for 2012 should be final by the fall of 2013.</p>\r\n<p>For more information concerning EIA\'s annual coal data collection and estimation, see the individual footnotes in the Annual Coal Report. </p>\r\n';});

define('js/model/FootnoteProxy',[
	'puremvc',
	'js/model/RequestProxy',
	'text!templates/footnotes/shipments.html',
	'text!templates/footnotes/reserves_capacity.html',
	'text!templates/footnotes/market_sales.html',
	'text!templates/footnotes/import_export.html',
	'text!templates/footnotes/consumption.html',
	'text!templates/footnotes/mine_production.html'
], function(
	puremvc,
	RequestProxy,
	footnoteShipments,
	footnoteReserves,
	footnoteMarketSales,
	footnoteImportExport,
	footnoteConsumption,
	footnoteProduction
){
	return  puremvc.Proxy.$extend({
	
		getData : function() {
			var ret = this.$super();
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			ret.topic = requestProxy.getTopicId();
			
			return ret;
		},
		
		getUrlArgs : function() {
			var data = this.getData();
			var ret = "";
			
			$.each(data, function(key, value) {
				ret += key + "=" + value + "&";
			});
			
			return ret.length >= 2 ? ret.substring(0, ret.length - 1) : "";
		},
		
		getFootnotes : function() {
			var data = this.getData();
			
			switch(data.topic) {
				case 20:
				case 21:
				case 22:
				case 23:
				case 24:
				case 25:
				case 26:
					return footnoteConsumption;
				case 27:
				case 28:
				case 29:
					return footnoteMarketSales;
				case 30:
				case 31:
				case 32:
					return footnoteReserves;
				case 33:
				case 34:
				case 35:
				case 36:
				case 37:
				case 38:
					return footnoteProduction;
				case 39:
				case 40:
				case 41:
				case 42:
					return footnoteImportExport;
				case 44:
				case 45:
				case 46:
				case 47:
				case 48:
				case 49:
				case 50:
				case 51:
				case 52:
				case 53:
				case 54:
				case 55:
					return footnoteShipments;
			}
		},
		
		__classvars__ : {
			NAME : 'FootnoteProxy'
		}
	});
});
define('js/view/MediatorTopicSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent, top){
			this.$super(this.$class.NAME + (top ? '_TOP' : '_BOTTOM'), viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().topicSelectionChanged.add(function(topicId){
				self.sendNotification(Facade.TOPIC_SELECTION_UPDATED, {'topic' : topicId}, 'TopicChanged');
			});
			
			this.getViewComponent().reportSelectionChanged.add(function(paramObj){
				self.sendNotification(Facade.REPORT_SELECTION_UPDATED, paramObj, 'ReportChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLE_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			switch(note.getName()){
				case Facade.TABLE_PARAMS_UPDATED:
					this.getViewComponent().create(
						parameterProxy.getTopicId(),
						parameterProxy.getReportIndex(),
						configProxy.getTopics(),
						configProxy.getReports()
					);
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'TopicSelectionMediator'
		}
	});
});
define('js/view/MediatorCoalTopicSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/view/MediatorTopicSelection'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	MediatorTopicSelection
) {
	return  MediatorTopicSelection.$extend({
		
		onRegister : function(){
			var self = this;
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			this.getViewComponent().topicSelectionChanged.add(function(newTopicId){
				var currentTopicId = parameterProxy.getTopicId();
				
				if((newTopicId >= 44) && (newTopicId <= 55)){
					if(
						(
							currentTopicId < 44 || 
							currentTopicId > 55
						) || (
							(currentTopicId % 2) == 1
						) && (
							(newTopicId % 2) == 0
						) 
					  )
					{
						newTopicId++;
					}
				}
				if((newTopicId >= 39) && (newTopicId <= 42)) {
					parameterProxy.updateChartType('map');
				}
				self.sendNotification(Facade.TOPIC_SELECTION_UPDATED, {'topic' : newTopicId}, 'TopicChanged');
			});
			
			this.getViewComponent().reportSelectionChanged.add(function(paramObj){
				self.sendNotification(Facade.REPORT_SELECTION_UPDATED, paramObj, 'ReportChanged');
			});
		},
		
		arrangeTopics : function(topics, selectedTopic) {
			var topicGroups = [
				{label : 'Coal mine production (MSHA)', topics : []},
				{label : 'Shipments of coal to the electric power sector (EIA-923 schedule 2)', topics : []},
				{label : 'Imports & exports of coal (U.S. Census Bureau)', topics : []},
				{label : 'Coal consumption and quality (EIA-3/EIA-5/EIA-923)', topics : []},
				{label : 'Coal market (EIA-7A)', topics : []},
				{label : 'Coal reserves & capacity (EIA-7A)', topics : []}
			];
			
			$.each( topics, function(index, topic) {
				switch(topic.dataTopicId) {
					case 20:
					case 21:
					case 22:
					case 23:
					case 24:
					case 25:
					case 26:
						topicGroups[3].topics.push(topic);
						break;
					case 27:
					case 28:
					case 29:
						topicGroups[4].topics.push(topic);
						break;
					case 30:
					case 31:
					case 32:
						topicGroups[5].topics.push(topic);
						break;
					case 33:
					case 34:
					case 35:
					case 36:
					case 37:
					case 38:
						topicGroups[0].topics.push(topic);
						break;
					case 39:
					case 40:
						break;
					case 41:
						topic.description = 'Quantity & price of coal imports & exports'
						topicGroups[2].topics.push(topic);
						break;
					case 42:
					case 45:
					case 47:
					case 49:
					case 51:
					case 53:
					case 55:
						break;
					case 44:
					case 46:
					case 48:
					case 50:
					case 52:
						if(topic.description.indexOf(':') != -1) {
							topic.description = topic.description.split(',')[0].split(':')[1].trim();
							topic.description = topic.description.charAt(0).toUpperCase() + topic.description.slice(1);
						}
						topicGroups[1].topics.push(topic);
						break;
					case 54:
						if(topic.description.indexOf(':') != -1) {
							topic.description = topic.description.split(':')[1].trim();
						}
						if(topic.description.indexOf('/') == -1) {
							topic.description += '/plant';
						}
						topicGroups[1].topics.push(topic);
						break;
					default:
						topicGroups.push(topic);
						break;
				}
			});
			
			switch(selectedTopic) {
				case 39:
				case 40:
				case 41:
				case 42:
					selectedTopic = 41;
					break;
				case 45:
				case 47:
				case 49:
				case 51:
				case 53:
				case 55:
					selectedTopic--;
					break;
			}
			
			return {
				selectedTopic : selectedTopic,
				topicGroups : topicGroups
			};
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			switch(note.getName()){
				case Facade.TABLE_PARAMS_UPDATED:
					var topicInfo = this.arrangeTopics(configProxy.getTopics(), parameterProxy.getTopicId());
					
					this.getViewComponent().create(
						topicInfo.selectedTopic,
						parameterProxy.getReportIndex(),
						topicInfo.topicGroups,
						configProxy.getReports()
					);
					break;
				default:
					this.$super(note);
			}
		}
	});
});
define('js/view/MediatorForm',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().formSubmitted.add(function(params){
				self.getViewComponent().hideForm();
				self.sendNotification(Facade.TABLE_SELECTION_UPDATED, params, 'FormSubmitted');
			});
			this.getViewComponent().formCanceled.add(function(params){
				self.getViewComponent().hideForm();
			});
			this.getViewComponent().formButtonClicked.add(function(params){
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME),
				configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				
				var topic = $.extend(true, {}, configProxy.getTopic(parameterProxy.getTopicId()));
				var rtype = parameterProxy.getRegionType();
				
				for(var i = 0; i < topic.aggColumns.length; i++) {
					if(topic.aggColumns[i].filterGroup.filterGroupId == 13) {
						for(var j = 0; j < topic.aggColumns[i].filterGroup.filterValues.length; j++) {
							if(((rtype == 'b' && topic.aggColumns[i].filterGroup.filterValues[j].valueId < 68) ||
								(rtype == 's' && topic.aggColumns[i].filterGroup.filterValues[j].valueId >= 68)) &&
								topic.aggColumns[i].filterGroup.filterValues[j].valueId != 0) {
								topic.aggColumns[i].filterGroup.filterValues.splice(j, 1);
								j--;
							}
						}
					}
				}
				
				self.getViewComponent().showForm(
					topic,
					parameterProxy.getFormParams()
				);
			})
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.RELOAD,
				Facade.REGION_TYPE_PARAM_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();
			switch(note.getName()){
				case Facade.RELOAD:
					vc.hideForm();
					break;
				case Facade.REGION_TYPE_PARAM_UPDATED:
					if(vc.opened) {
						vc.formButtonClicked.dispatch();
					}
					break;
				default:
					this.$super.handleNotification(note);
			}
		},
		
		__classvars__ : {
			NAME : 'FormMediator'
		}
	});
});
define('js/view/MediatorDownload',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;

			this.getViewComponent().downloadButtonClicked.add(function(params){
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME),
				configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				if(typeof configProxy.getCurrentView == 'function')
					self.getViewComponent().showDownload(
						configProxy.getDownloadParameters()[configProxy.getCurrentView()]
					);
				else
					self.getViewComponent().showDownload(configProxy.getDownloadParameters());
			});
			
			this.getViewComponent().downloadOptionClicked.add(function(params){
				self.getViewComponent().hideDownload();
				self.sendNotification(Facade.DOWNLOAD_OPTION_SELECTED, params);
			});
		},
		
		__classvars__ : {
			NAME : 'DownloadMediator'
		}
	});
});
define('js/view/MediatorVisualization',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.CHART_PARAMS_UPDATED,
				Facade.REGION_TYPE_PARAM_UPDATED,
				Facade.RELOAD
			]);
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.REGION_TYPE_PARAM_UPDATED:
				case Facade.CHART_PARAMS_UPDATED:
				case Facade.RELOAD:
					this.updateViewComponent();
					break;
				default:
					this.$super(note);
			}
		},
		
		onRegister : function(){
			var self = this;

			this.updateViewComponent();

			this.getViewComponent().visualizationSelected.add(function(params) {
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var notificationBody = {
					'ctype' : params.newClass
				};

				//check if rtype is changed and if it is applicable to the current page
				if(typeof params.item.rtype != 'undefined' && typeof parameterProxy.getRegionType == 'function' )
				{
					var rtype = params.item.rtype;
					//check if rtype has changed
					if(rtype != parameterProxy.getRegionType()){
						notificationBody['rtype'] = rtype;

						var configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
						var topic = configProxy.getTopic(parameterProxy.getTopicId());
						var rtype = params.item.rtype;
						var geo = [];
						notificationBody.geo = "";

						//build geo array
						for(var i = 0; i < topic.aggColumns.length; i++) {
							if(topic.aggColumns[i].filterGroup.filterGroupId == 13) {
								for(var j = 0; j < topic.aggColumns[i].filterGroup.filterValues.length; j++) {
									var fv = topic.aggColumns[i].filterGroup.filterValues[j];
									if(((rtype == 's' && fv.valueId < 68) || (rtype == 'b' && fv.valueId >= 68)) || fv.valueId == 0) {
										geo[fv.valueId] = 1;
									} else {
										geo[fv.valueId] = 0;
									}
								}
							}
						}
						var binaryStr = "";
						//fill in missing array elements and ensure the length is a multiple of 5
						for(var i =0; i < Math.ceil(geo.length/5)*5; i++){
							//fill in missing array elements with a zero
							if(i >= geo.length || typeof geo[i] == 'undefined') {
								geo[i] = 0;
							}
							//build 5 character binary string, have to do 5 characters at a time because leading zero's would be dropped otherwise
							binaryStr += geo[i];

							if(i%5 == 4) {
								// parse binary to base 32 every 5 characters
								notificationBody.geo += parseInt(binaryStr, 2).toString(32);
								binaryStr = "";
							}
						}
					}
				}
				self.sendNotification(Facade.CHART_SELECTION_UPDATED, notificationBody);
			});
		},

		updateViewComponent : function() {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);

			var visualizations = configProxy.getVisualizations().slice();
			if(typeof parameterProxy.hasStateBasinFilterGroup == 'function'){
				if(parameterProxy.hasStateBasinFilterGroup(configProxy.getTopic(requestProxy.getTopicId()))) {
					visualizations.push({
						'name' : 'visualizations',
						'class' : 'map',
						'description' : 'Coal basin map over time',
						'label' : 'Coal basin map',
						'rtype' : 'b'
					});
				}
			}	
			var query = parameterProxy.getQuery();
			var selectedIndex = 0;

			for(var i = 0; i < visualizations.length; i++) {
				if(visualizations[i]['class'] == query['ctype']) {
					if(visualizations[i]['class'] == 'map') {
						if(visualizations[i].rtype == query['rtype'] || typeof query['rtype'] == 'undefined') {
							selectedIndex = i;
							break;
						}
					}
					else {
						selectedIndex = i;
						break;
					}
				}
			}
			this.getViewComponent().generateMenu(
				this.getViewComponent().container,
				{
					items : visualizations,
					currentSelectionIndex : selectedIndex
				}
			);
		},

		__classvars__ : {
			NAME : 'VisualizationMediator'
		}
	});
});
define('js/view/MediatorPinKeyVisualization',[
	'puremvc',
	'js/Facade',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		listNotificationInterests : function(){
			return [
				Facade.PIN_KEY_PARAM_UPDATED
			];
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			switch(note.getName()){
				case Facade.PIN_KEY_PARAM_UPDATED:
					this.getViewComponent().updateSelection(parameterProxy.getLinkType());
					break;
				default:
					this.$super(note);
			}
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().visualizationSelected.add(function(params) {
				self.sendNotification(Facade.PIN_KEY_SELECTION_UPDATED, {
					'ltype' : params.newClass
				});
			});
		},
		
		__classvars__ : {
			NAME : 'PinKeyVisualizationMediator'
		}
	});
});
define('js/view/MediatorViewStack',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		 __init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		 },
		 
		 onRegister : function() {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			if(typeof configProxy.setViews == 'function') {
				configProxy.setViews(this.getViewComponent().viewNames);
			}
		 },
		 
		 listNotificationInterests : function(){
			return this.$super().concat([
				Facade.UPDATE_VISUALIZATION_VIEW_STACK
			]);
		 },
		 
		 handleNotification : function(note){
			var vc = this.getViewComponent();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			switch(note.getName()){
				case Facade.UPDATE_VISUALIZATION_VIEW_STACK:
					var viewName = parameterProxy.getChartType();
					if(vc.hasView(viewName)) {
						if(typeof configProxy.setCurrentView == 'function') {
							configProxy.setCurrentView(viewName);
						}
						vc.setActiveView(viewName);
					}
					break;
				default:
					this.$super(note);
					break;
			}
		 },
		 
		 __classvars__ : {
			NAME : 'ViewStackMediator'
		 }
	});
});
define('js/view/MediatorLineChart',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().indexingChanged.add(function(value){
				self.sendNotification(Facade.CHART_SELECTION_UPDATED, {
					'chartindexed': value
				});
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.CHART_DATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.UNLOAD,
				Facade.DOWNLOAD_OPTION_SELECTED
			]);
		},
		
		handleNotification : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			timeParams = parameterProxy.getTimeParams(),
			chartParams = parameterProxy.getChartParams(),
			chartType = parameterProxy.getChartType();
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
				case Facade.CHART_DATA_UPDATED:
					if(chartType == 'linechart' || chartType == 'pin' || chartType == 'sourcekey')
						var arePinnedSeriesCharted = parameterProxy.arePinnedSeriesCharted('linechart');
						var userGenerated = parameterProxy.isUserGenerated('linechart');
						this.getViewComponent().update(
							(arePinnedSeriesCharted ? 'Custom Chart' : tableDataProxy.getTableTitle()) + ', ' + parameterProxy.getFreqTitle(),
							tableDataProxy.getChartData('linechart'),
							tableDataProxy.getDataColumns(),
							timeParams['freq'],
							chartParams['chartindexed'],
							timeParams['start'],
							timeParams['end'],
							arePinnedSeriesCharted,
							userGenerated
						);
					break;
				case Facade.TIME_PARAMS_UPDATED:
					if(!tableDataProxy.isUpdating && (chartType == 'linechart' || chartType == 'pin' || chartType == 'sourcekey')){
						this.getViewComponent().setStartEnd(
							timeParams['start'],
							timeParams['end'],
							timeParams['freq']);
					}
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'linechart'){
                        if(note.body.type == 'embed') {
                            this.getViewComponent().embed();
                        }
                        else {
                            this.getViewComponent().download(note.body.type);
                        }
					}
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'ChartMediator'
		}
	});
});
define('js/view/MediatorColumnChart',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().endpointSelectionChanged.add(function(value){
				self.sendNotification(Facade.CHART_SELECTION_UPDATED, {
					'columnendpoints': value
				});
			});
			this.getViewComponent().valuesSelectionChanged.add(function(value){
				self.sendNotification(Facade.CHART_SELECTION_UPDATED, {
					'columnvalues': value
				});
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.CHART_DATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.UNLOAD,
				Facade.DOWNLOAD_OPTION_SELECTED
			]);
		},
		
		handleNotification : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			timeParams = parameterProxy.getTimeParams(),
			chartParams = parameterProxy.getChartParams(),
			chartType = parameterProxy.getChartType();
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
				case Facade.CHART_DATA_UPDATED:
					
					if(chartType == 'columnchart' || chartType == 'pin' || chartType == 'sourcekey')
						var arePinnedSeriesCharted = parameterProxy.arePinnedSeriesCharted('columnchart');
						var userGenerated = parameterProxy.isUserGenerated('columnchart');
						this.getViewComponent().update(
							(arePinnedSeriesCharted ? 'Custom chart' : tableDataProxy.getTableTitle()) + ', ' + parameterProxy.getFreqTitle().toLowerCase(),
							tableDataProxy.getChartData('columnchart'),
							tableDataProxy.getDataColumns(),
							timeParams['freq'],
							timeParams['start'],
							timeParams['end'],
							chartParams['columnendpoints'],
							chartParams['columnvalues'],
							arePinnedSeriesCharted,
							userGenerated
						);
					break;
				case Facade.TIME_PARAMS_UPDATED:
					if(!tableDataProxy.isUpdating && (chartType == 'columnchart' || chartType == 'pin' || chartType == 'sourcekey')){
						this.getViewComponent().setStartEnd(
							timeParams['start'],
							timeParams['end'],
							timeParams['freq']);
					}
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'columnchart'){
                        if(note.body.type == 'embed') {
                            this.getViewComponent().embed();
                        }
                        else {
                            this.getViewComponent().download(note.body.type);
                        }
					}
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'ColumnChartMediator'
		}
	});
});
define('js/view/MediatorDualSliderTimeSelection',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().timeSelectionChanged.add(function(start, end){
				self.sendNotification(Facade.TIME_SELECTION_UPDATED, {
					'start' : start,
					'end' : end
				});
			});
			
			this.getViewComponent().frequencySelectionChanged.add(function(freq){
				self.sendNotification(Facade.FREQUENCY_SELECTION_UPDATED, {
					'freq': freq
				}, 'FrequencyChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TIME_PARAMS_UPDATED,
				Facade.TABLEDATA_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			timeParams = parameterProxy.getTimeParams(),
			topic = configProxy.getTopic(parameterProxy.getTopicId()),
			dataColumns;
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					dataColumns = tableDataProxy.getDataColumns();
					this.getViewComponent().create(
						topic,
						timeParams['freq'],
						dataColumns,
						timeParams['start'],
						timeParams['end']
					);
					break;
				case Facade.TIME_PARAMS_UPDATED:
					if(!tableDataProxy.isUpdating){
						dataColumns = tableDataProxy.getDataColumns();
						this.getViewComponent().updateTimeSelection(
							timeParams['freq'],
							dataColumns,
							timeParams['start'],
							timeParams['end']
							
						);
					}
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'TimeSelectionMediator'
		}
	});
});
define('js/view/MediatorHelpTooltip',[
	'puremvc',
	'js/Facade',
	'js/model/HelpTooltipProxy'
	
], function(
	puremvc,
	Facade,
	HelpTooltipProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.UNLOAD
			]);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().helpButtonClicked.add(function(){
				var helpTooltipProxy = self.facade.retrieveProxy(HelpTooltipProxy.NAME);
				self.getViewComponent().toggleHelpTooltips(helpTooltipProxy.getHelpTooltips());
			});
		},
		
		handleNotification : function(note){			
			switch(note.getName()){
				case Facade.UNLOAD:
					this.getViewComponent().hideHelpTooltips();
					break;
				default:
					this.$super(note);
			}
		},
		
		onRemove : function() {
			this.$super();
			this.getViewComponent().hideHelpTooltips();
		},
		
		__classvars__ : {
			NAME : 'HelpTooltipMediator'
		}
		
	});
});
define('js/view/MediatorAnimatedMap',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/MapDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'us-merc-en',
	'coal-basin-merc-en'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	MapDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return puremvc.Mediator.$extend({
		__init__ : function(viewComponent, name){
			this.$super((typeof name == 'undefined' ? this.$class.NAME : name), viewComponent); 
		},
		
		onRegister : function(){
			var self = this;
			
			this.viewComponent.mapTypeChangeRequested.add(function(mapType){
				self.sendNotification(Facade.MAP_TYPE_SELECTION_UPDATED, {maptype: mapType});
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.MAP_DATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.MAP_TYPE_PARAM_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.REGION_TYPE_PARAM_UPDATED,
				Facade.UNLOAD
			]);
		},
		
		handleNotification : function(note){
			switch(note.name){
				case Facade.TIME_PARAMS_UPDATED:
				case Facade.MAP_DATA_UPDATED:
				case Facade.MAP_TYPE_PARAM_UPDATED:
				case Facade.REGION_TYPE_PARAM_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
					var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var topic = configProxy.getTopic(parameterProxy.getTopicId());
			
					if(topic.drillDownTopicId != "") {
						this.viewComponent.container.addClass('clickable-regions').removeClass('clickable-markers');
					}
					else {
						this.viewComponent.container.removeClass('clickable-regions').removeClass('clickable-markers');
					}
					
					if(!tableDataProxy.isUpdating && mapDataProxy.hasData){
						var self = this,
						timeParams = parameterProxy.getTimeParams(),
						data = mapDataProxy.getData();
                        var embedOptions = {
                            'geoset_id' : tableDataProxy.getChartData('map')[0].GEOSET_ID,
                            'type' : 'map',
                            'date_mode' : 'all'
                        };

                        var minPeriod = function(t,f){
                            switch(f){
                                case 'A':
                                    return t.annualStart;
                                case 'M':
                                    return t.monthlyStart;
                                case 'Q':
                                    return t.quarterlyStart;
                            }
                        }(topic, timeParams.freq);

                        var maxPeriod = function(t,f){
                            switch(f){
                                case 'A':
                                    return t.annualEnd;
                                case 'M':
                                    return t.monthlyEnd;
                                case 'Q':
                                    return t.quarterlyEnd;
                            }
                        }(topic, timeParams.freq);

                        if(timeParams.end != maxPeriod) {
                            embedOptions['date_mode'] = 'range';
                            embedOptions['start'] = (timeParams.freq == 'Q') ? timeParams.start.toString().substr(1, 1) + 'Q' + timeParams.start.toString().substr(2, 4) : timeParams.start.toString();
                            embedOptions['end'] = (timeParams.freq == 'Q') ? timeParams.end.toString().substr(1, 1) + 'Q' + timeParams.end.toString().substr(2, 4) : timeParams.end.toString();
                        }
                        else if(timeParams.start != minPeriod) {
                            embedOptions['date_mode'] = 'start';
                            embedOptions['start'] = (timeParams.freq == 'Q') ? timeParams.start.toString().substr(1, 1) + 'Q' + timeParams.start.toString().substr(2, 4) : timeParams.start.toString();
                        }


                        this.viewComponent.update(
							{
								"DATA" : parameterProxy.getRegionType() == 'b' ? data.BASIN_DATA : data.DATA,
								"DATACOLUMNS" : data.DATACOLUMNS,
								"PRECISION" : data.PRECISION,
								"TITLE" : data.TITLE,
								"UNITS" : data.UNITS
							},
							parameterProxy.getMapType(),
							timeParams.start,
							timeParams.end,
							timeParams.freq,
							{ 
								'onRegionOver' : function(e, code) {
									if(code.split('-')[0] == 'US' && parameterProxy.getRegionType() == 'b') {
										return false;
									}
									return true;
								},
								'regionStyle' : {
									hover : {
										stroke : '#FFFF96',
										"fill-opacity" : 1,
										"stroke-width" : 3,
										"stroke-opacity" : 1
									}
								},
                                'embedOptions' : embedOptions
							},
							parameterProxy.getRegionType() == 'b' ? 'coal_basin_merc_en' : 'us_merc_en'
						);
						if(topic.drillDownTopicId != "") {
							this.getViewComponent().map.regionClicked.add(function(stateCode, event){
								var filterArray = [];
								var binaryString = "";
								var datum = tableDataProxy.getChartData().map[0];
								var position = self.getViewComponent().position;
								
								if(stateCode.match(/(US-[A-Z][A-Z])|(BS-[A-Z][A-Z][A-Z])/)) {
									stateCode = stateCode.split('-')[1];
									
									var filterParams = function(q){
										var ret = {};
										var pairs = $.map(q.split('&'), function(p){
											return [p.split('=')];
										});
										$.each(pairs, function(i, p){
											ret[p[0]] = p[1];
										});
										return ret;
									}(datum.filterQuery);
									
									for(var i = 0; i < topic.aggColumns.length; i++) {
										if(topic.aggColumns[i].filterGroup.varName == 'geo') {
											var filterValues = topic.aggColumns[i].filterGroup.filterValues;
											for(var j = 0; j < filterValues.length; j++) {
												if(filterValues[j].filterValue == stateCode) {
													filterArray[filterValues[j].valueId] = 1;
													break;
												}
											}
											break;
										}
									}
									
									filterParams['geo'] = '';
									
									for(i = 0; i < Math.ceil(filterArray.length / 5) * 5; i++) {
										if(filterArray[i] && filterArray[i] == 1) {
											binaryString += "1";
										}
										else {
											binaryString += "0";
										}
										if((i % 5) == 4) {
											filterParams['geo'] = filterParams['geo'] + parseInt(binaryString, 2).toString(32);
											binaryString = "";
										}
									}
									filterParams['geo'] = (filterParams['geo'] == "") ? "g" : filterParams['geo'];
									
									var label = 'topic/'+ topic.drillDownTopicId + '?agg=' + datum.agg;
									$.each(filterParams, function(key, value) {
										label += '&' + key + '=' + value;
									});
									label += '&freq=' + parameterProxy.getFreq() + '&datecode=' + position;
									
									var body = {
										topic : topic.drillDownTopicId,
										params : $.extend({
											agg : datum.agg,
											freq : parameterProxy.getFreq(),
											datecode : position
										}, filterParams),
										label : label
									};
									
									self.sendNotification(Facade.AGGREGATE_DATA_CLICK_THROUGH, body, 'AggregateClickThrough');
								}
							});
						}
					}
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'map'){
                        switch(note.body.type) {
                            case 'csv':
                                var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
                                var mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
                                data = mapDataProxy.getData();
                                var csvData = [
                                        ['"' + data.TITLE + '"'],
                                        ['"' + window.location.href + '"'],
                                        ['"' + Date() + '"'],
                                        ['"Source: U.S. Energy Information Administration"']
                                    ].concat(this.getViewComponent().outputToArrOfArrs());
                                this.sendNotification(Facade.CSV_DATA_GENERATED, csvData);
                                break;
                            case 'png':
                            case 'pdf':
                                this.getViewComponent().map.exportMap(note.body.type);
                                break;
                            case 'svg':
                                if(window.SVGAngle)
                                    this.getViewComponent().map.exportMap(note.body.type);
                                else
                                    alert("Map download requires a modern browser, such as Chrome, Firefox or Internet Explorer 9+");
                                break;
                            case 'embed':
                                this.getViewComponent().embed();
                                break;
                        }
                    }
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				default:
					this.$super(note);
			}
		},
		
		
		onRemove : function() {
			this.$super();
			this.getViewComponent().destroy();
		},
		
		__classvars : {
			NAME : 'AnimatedMapMediator'
		}
	});
});
define('js/view/MediatorAnalysis',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy'
], function(
	puremvc,
	Facade,
	TableDataProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;

			this.getViewComponent().analysisButtonClicked.add(function(){
				var tableDataProxy = self.facade.retrieveProxy(TableDataProxy.NAME);
				if(!tableDataProxy.isUpdating)
					self.getViewComponent().showAnalysis(
						tableDataProxy.getRelatedAnalysis(),
						tableDataProxy.getMoreAnalysisURL()
					);
			});
		},
		
		__classvars__ : {
			NAME : 'AnalysisMediator'
		}
	});
});
define('js/view/DateCodeUtil',[], function() {
	return {
		months : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
		
		parseDateCode : function(code, freq){
			// Make sure we are dealing with a string
			code = code.toString();
			var	y = code.substring(0,4),
			date, m;
			
			switch(freq.toUpperCase()){
				case 'A':
					m = 0;
					break;
				case 'Q':
					m = (code.substring(4) - 1) * 3;
					break;
				case 'M':
					m = code.substring(4) - 1;
					break;
			}
			
			if(m !== void 0)
				date = new Date(y, m);
			return date;
		},
		
		createDateCode : function(date, freq){
			var y = date.getFullYear().toString(),
			code, qm;
			
			switch(freq.toUpperCase()){
				case 'A':
					qm = '';
					break;
				case 'Q':
					qm = Math.ceil((date.getMonth() + 1) / 3)
					qm = qm < 10 ? '0' + qm : qm;
					break;
				case 'M':
					qm = date.getMonth() + 1;
					qm = qm < 10 ? '0' + qm : qm;
					break;
			}
			
			if(qm !== void 0)
				code = y + '' + qm;
			return code;
		},
		
		formatDateCode : function(code, freq){
			var y = code.toString().substring(0,4),
			qm = code.toString().substring(4,6),
			ret = '';
			
			switch(freq){
				case 'A':
					ret = y;
					break;
				case 'Q':
					ret = 'Q' + parseInt(qm, 10) + ' ' + y;
					break;
				case 'M':
					ret = this.months[parseInt(qm, 10) - 1] + ' ' + y;
					break;
			}
			
			return ret;
		},
		
		deincrementDateCode : function(code, freq){
			return this.deincrementOrIncrementDateCode(code, freq, false);
		},
		
		incrementDateCode : function(code, freq){
			return this.deincrementOrIncrementDateCode(code, freq, true);
		},
		
		deincrementOrIncrementDateCode : function(code, freq, inc){
			var d = this.parseDateCode(code, freq);
			switch(freq){
				case 'A':
					d = new Date(d.getFullYear() + (inc ? 1 : -1), 0);
					break;
				case 'Q':
					d = new Date(d.getFullYear(), d.getMonth() + (inc ? 3 : -3));
					break;
				case 'M':
					d = new Date(d.getFullYear(), d.getMonth() + (inc ? 1 : -1));
					break;
			}
			
			return this.createDateCode(d, freq);
		},
		
		getNumberOfDataPoints : function(start, end, freq) {
			var ret = 0;
			var startDate = this.parseDateCode(start, freq);
			var endDate = this.parseDateCode(end, freq);
			
			switch(freq) {
				case 'A':
					ret = endDate.getFullYear() - startDate.getFullYear() + 1;
					break;
				case 'Q':
					ret = (endDate.getFullYear() - startDate.getFullYear() + 1)*4 - startDate.getMonth()/3 - (3 - endDate.getMonth()/3);
					break;
				case 'M':
					ret = (endDate.getFullYear() - startDate.getFullYear() + 1)*12 - startDate.getMonth() - (11 - endDate.getMonth());
					break;
			}
			
			return ret;
		}
	};
});
define('js/view/GridFormatters',[], function(){
	var self = {
		nestedPropertyResolver : function(field, datum){
			var accessorArray = field.split('.');
			var ret = datum;
			for(var i = 0; i < accessorArray.length; i++) {
				ret = ret[accessorArray[i]];
				if(!ret || typeof ret == 'undefined')
					break;
			}
			return ret;
		},
		
		descriptionFormatter : function(row, cell, value, columnDef, datum){
			var level = datum['LEVEL'];
			var levelGroups = datum['LEVEL_GROUPS'];
			var wrapper = '<span class="description_wrapper ' +
				// Add the level group class to the wrapper
				(levelGroups ? 'level_group_' + level + '_' + levelGroups[level] : '') + '">';
			var textWrapper = '<span class="description_text_wrapper ' +  (level === void 0 ? 'bold' : '' ) + '"';
			if(datum['CHART_NAME'])
				textWrapper += ' title="' + datum['CHART_NAME'];
			textWrapper += '">';
			var closeWrapper = '</span></span>';
			var spacers = '';
			var j, k, spacer;
			
			// Loop through the levels below the current level to add spacers
			for(j=level; j > 0; j--){
				// Add the spacer
				spacer = '<span class="description_spacer';
	
				if(levelGroups && levelGroups[j] !== void 0 || j == level)
					spacer += ' spacer_group_' + j + '_' + levelGroups[j];
				// if we are on a level other than the datum level
				if(j != level){
					// if there is a next value, it is a pre spacer, otherwise
					// it is a blank spacer
					if(levelGroups[j] !== void 0)
						spacer += ' pre';
					else
						spacer += ' blank';
				}
				// if we are on the datum level
				else{
					// if there is a next value, it is an intermediate
					// spacer, otherwise it is a last spacer
					if(!datum['IS_LAST'])
						spacer += ' intermediate';
					else
						spacer += ' last';
				}
				
				spacer += '"></span>';
				spacers = spacer + spacers;
			}
			
			return wrapper + spacers + textWrapper +  (!datum['PINNED'] ? value : datum['PINNED_NAME']) + (datum['PINNED'] || datum['SHOW_UNITS'] ? ' (' + datum['UNITS'] + ')' : '') + closeWrapper;
		},
		
		numericalDataFormatter : function(row, cell, value, columnDef, datum){
			if(!datum.HAS_DATA) { return '';}
			var ret = (isNaN(parseFloat(value)) ? value :
					   Highcharts.numberFormat(
							value,
							datum['PRECISION'] !== void 0 ? datum['PRECISION'] : 0,
							void 0,
							','
						)
					);
			var rse = columnDef['rseField'] ? self.nestedPropertyResolver(columnDef['rseField'], datum) : void 0;
			return ((ret === void 0 || ret == '') ? '--' : ret) + (columnDef.showRSE ? ' (' + (rse !== void 0 ? rse : '--') + ')' : '');
		},
		
		rseNumericalDataFormatter : function(row, cell, value, columnDef, datum){
			
		},
		
		chartButtonFormatter : function(row, cell, value, columnDef, dataContext){
			var cssClass = 'ico';
			var title = '';
			
			if(columnDef.iconClass === 'map' && ((dataContext.SERIES_ID && dataContext.GEOSET_ID == null) || !value || dataContext.ACTION)) {
				return '';
			}
			else if (dataContext.ACTION){
				cssClass += ' remove';
				title = 'Click to clear chart';
				return '<span class="' + cssClass + '" title="' + title + '"></span>';
			}
			else {
				cssClass += ' ' + columnDef.iconClass;
				if(columnDef.iconClass == 'map') {
					if(dataContext[columnDef.iconClass]) {
						cssClass += ' selected';
					}
					title = 'Click to view a U.S. map of this series';
				}
				else if(value) {
					if(dataContext[columnDef.iconClass]) {
						cssClass += ' selected';
						title = 'Click to remove this series from the chart';
					}
					else {
						title = 'Click to add this series to the chart';
					}
				}
				else {
					cssClass += ' parent';
					if(dataContext[columnDef.iconClass]) {
						cssClass += ' selected';
						title = 'Click to remove all child series from the chart';
					}
					else {
						title = 'Click to add all child series to the chart';
					}
				}
				
				return value || dataContext.LEVEL_GROUPS ? '<span class="'  + cssClass + '" title="' + title + '"></span>' : '';
			}
		},
		
		pinSeriesFormatter : function(row, cell, value, columnDef, dataContext) {
			var cssClass = 'ico';
			var title = '';
			
			if(columnDef.iconClass == 'sourcekey' && (!value || dataContext.ACTION)) {
				return '';
			}
			if(dataContext.ACTION && columnDef.iconClass == 'pin') {
				cssClass += ' remove';
				title = 'Click to remove all pinned series';
				return '<span class="' + cssClass + '" title="' + title + '"></span>';
			}
			else {
				cssClass += ' ' + columnDef.iconClass;
				if(columnDef.iconClass == 'sourcekey') {
					title = 'Click to view series in API browser';
				}
				else if(value) {
					if(dataContext[columnDef.iconClass]) {
						cssClass += ' selected';
						title = 'Click to unpin this series';
					}
					else {
						title = 'Click to pin this series for comparison across data sets';
					}
				}
				else {
					cssClass += ' parent';
					if(dataContext[columnDef.iconClass]) {
						cssClass += ' selected';
						title = 'Click to unpin all child series';
					}
					else {
						title = 'Click to pin all child series for comparison across data sets';
					}
				}
				
				return value || dataContext.LEVEL_GROUPS ? '<span class="' + cssClass + '" title="' + title + '"></span>' : '';
					
			}
		},
		
		sourceKeyFormatter : function(row, cell, value, columnDef, dataContext) {
			return value || dataContext.LEVEL_GROUPS ? '<span class="ico ' + 
				columnDef.iconClass +
				(value ? '' : ' parent') + 
				(dataContext[columnDef.iconClass] ? ' selected' : '') + 
				'"></span>' : '';
		}
	};
	
	return self;
});
/*
 RequireJS order 1.0.5 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
 Available via the MIT or new BSD license.
 see: http://github.com/jrburke/requirejs for details
*/
(function(){function k(a){var b=a.currentTarget||a.srcElement,c;if(a.type==="load"||l.test(b.readyState)){a=b.getAttribute("data-requiremodule");j[a]=!0;for(a=0;c=g[a];a++)if(j[c.name])c.req([c.name],c.onLoad);else break;a>0&&g.splice(0,a);setTimeout(function(){b.parentNode.removeChild(b)},15)}}function m(a){var b,c;a.setAttribute("data-orderloaded","loaded");for(a=0;c=h[a];a++)if((b=i[c])&&b.getAttribute("data-orderloaded")==="loaded")delete i[c],require.addScriptToDom(b);else break;a>0&&h.splice(0,
a)}var f=typeof document!=="undefined"&&typeof window!=="undefined"&&document.createElement("script"),n=f&&(f.async||window.opera&&Object.prototype.toString.call(window.opera)==="[object Opera]"||"MozAppearance"in document.documentElement.style),o=f&&f.readyState==="uninitialized",l=/^(complete|loaded)$/,g=[],j={},i={},h=[],f=null;define('order',{version:"1.0.5",load:function(a,b,c,e){var d;b.nameToUrl?(d=b.nameToUrl(a,null),require.s.skipAsync[d]=!0,n||e.isBuild?b([a],c):o?(e=require.s.contexts._,!e.urlFetched[d]&&
!e.loaded[a]&&(e.urlFetched[d]=!0,require.resourcesReady(!1),e.scriptCount+=1,d=require.attach(d,e,a,null,null,m),i[a]=d,h.push(a)),b([a],c)):b.specified(a)?b([a],c):(g.push({name:a,req:b,onLoad:c}),require.attach(d,null,a,k,"script/cache"))):b([a],c)}})})();





null;





/**
 *	The 'datagrid' module does stuff.
 *
 *	@module datagrid
 *	@requires signals, classy, dragevent, mousewheel, slickcore, slickgrid
 */
 !function(define){
	define(
	'datagrid',[
		'signals',
		'classy',
		'order!dragevent',
		'order!mousewheel',
		'order!slickcore',
		'order!slickgrid',
		'order!slickdataview'
	],
	function(signals, Class)
	{
		/**
		 * The DataGrid view component
		 *
		 * @class DataGrid
		 * @namespace datagrid
		 * @constructor
		 * @param container {jQuery} A jQuery wrapper for the container
		 * that this view component should render to.
		 **/
		return Class.$extend({
			/**
			 *	Constructor Function for datagrid
			 *	@method __init__
			 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the datagrid component.
			 */
			__init__ : function(container){
				var self = this;
				/**
				 * A flag describing whether or not this dataGrid instance
				 * has been filtered
				 *
				 * @property isFiltered
				 * @type {boolean}
				 **/
				this.isFiltered = false;
				/**
				 * A reference to the SlickGrid instance proxied by this
				 * dataGrid instance.
				 *
				 * @property slickGrid
				 * @type {SlickGrid}
				 **/
				this.slickgrid = null;
				/**
				 * A reference to the container that this dataGrid instance
				 * renders to
				 *
				 * @property container
				 * @type {jQuery}
				 * @final
				 **/
				this.container = container.empty();
				/**
				 * The innerGridContainer that the SlickGrid instance renders
				 * to.
				 *
				 * @property innerGridContainer
				 * @type {jQuery}
				 * @final
				 **/
				this.innerGridContainer = $(this.$class.innerSlickGridHTML)
					.appendTo(this.container);
				/**
				 * The vertical slider object for this dataGrid instance.
				 *
				 * @property vSlider
				 * @type {object}
				 **/
				this.vSlider = null;
				/**
				 * The top horizontal slider object for this dataGrid instance.
				 *
				 * @property hSliderTop
				 * @type {object}
				 **/
				this.hSliderTop = null;
				/**
				 * The bottom horizontal slider object for this dataGrid instance.
				 *
				 * @property hSliderBottom
				 * @type {object}
				 **/
				this.hSliderBottom = null;
				/**
				 * The columns for this dataGrid after filtering has been applied.
				 *
				 * @property filteredColumns
				 * @type {array}
				 **/
				this.filteredColumns = [];
				/**
				 * The columns for this dataGrid without filtering applied.
				 *
				 * @property columns
				 * @type {array}
				 **/
				this.columns = [];
				/**
				 * The data array rendered by this dataGrid instance.
				 *
				 * @property data
				 * @type {array}
				 **/
				this.data = [];
				/**
				 * The configuration object for this dataGrid instance.
				 *
				 * @property options
				 * @type {object}
				 **/
				this.options = {};
				/**
				 * The signal that is issued whenever the SlickGrid wrapped by this
				 * dataGrid instance is clicked.  Handlers are passed row index, the
				 * cell (column) index, and the corresponding data item as arguments.
				 *
				 * @property onClick
				 * @type {Signal}
				 **/
				this.onClick = new signals.Signal();
				/**
				 * The signal that is issued whenever the columns are reorderd in the
				 * wrapped SlickGrid
				 *
				 * @property onColumnOrderChange
				 * @type {Signal}
				 **/
				this.onColumnOrderChanged = new signals.Signal();
				
				this.onHeaderClick = new signals.Signal();

				this.onHeaderCellRendered = new signals.Signal();

				this.onViewRowCountChanged = function(e, args){
					self.slickgrid.updateRowCount();
					self.slickgrid.render();
				};
				
				this.onViewRowsChanged = function(e, args){
					self.slickgrid.invalidateRows(args.rows);
					self.slickgrid.render();
				};
				
			},
			
			update : function(data, columns, options, start, end){
				var self = this;
				var doNumericFilter = start !== void 0 && end !== void 0;
				var filteredCols;
				
				columns = typeof columns == 'undefined' ? this.columns : columns;
				options = typeof options == 'undefined' ? this.options : options;
				
				if(!this.slickgrid){
					this.setData(data);
					this.setColumns(columns);
					this.setOptions(options);

					if(doNumericFilter)
						filteredCols = this.filterNumericColumns(start, end);
					else
						filteredCols = this.filterHiddenColumns(this.options.addSpacerColumn ?
							this.columns.concat([this.$class.spacerColumnOps]) :
							this.columns
						);

					this.slickgrid = new Slick.Grid(
						this.innerGridContainer,
						this.data,
						filteredCols,
						this.options
					);
					this.slickgrid.onClick.subscribe(function(e, data){
						self.onClick.dispatch(
							data['row'],
							data['cell'],
							self.slickgrid.getDataItem(data['row']),
							e
						);
					});
					this.slickgrid.onSort.subscribe(function(e, args) {
						self.data.sort(function(row1, row2) {
							var field = args.sortCol.field;
							var sign = args.sortAsc ? 1 : -1;
							var value1 = row1[field], value2 = row2[field];
							var result = (value1 == value2 ? 0 : ((value1 > value2) ? 1 : -1)) * sign;
							// Nulls last
							result = (
								(typeof value1 == 'undefined' || (typeof value1 == 'string' && value1 == '')) ? 
									1 : (
									(typeof value2 == 'undefined' || (typeof value2 == 'string' && value2 == '')) ? 
										-1 : 
										result
									)
							);
							if(result != 0) {
								return result;
							}
							return 0;
						});
						
						self.slickgrid.invalidate();
						self.slickgrid.render();
					});
					this.slickgrid.onColumnsReordered.subscribe(function(e, args) {
						self.resetHSliders();
						self.checkScroll();
						self.onColumnOrderChanged.dispatch(e, args);
					});
					this.slickgrid.onColumnsResized.subscribe(function(e, args) {
						self.resetHSliders();
						self.checkScroll();
					});
					this.slickgrid.onHeaderClick.subscribe(function(e, args){
						self.onHeaderClick.dispatch(e, args);
					});
					this.slickgrid.onHeaderCellRendered.subscribe(function(e, args){
						self.onHeaderCellRendered.dispatch(e, args);
					});
					this.checkScroll();
					if(this.options.scrollToRight) this.scrollToRight();
				}
				else{
					this.setData(data);
					this.setColumns(columns, true);
					this.setOptions(options);
					if(doNumericFilter) {
						this.filterNumericColumns(start, end);
					}
					else {
						this.slickgrid.setColumns(columns);
					}
					this.resetHSliders();
					this.checkScroll();
				}
			},
			
			outputToArrOfArrs : function(filtered){
				filtered = filtered !== void 0 ? filtered : true;
				var cols = filtered && this.isFiltered ? this.filteredColumns : this.columns,
				ret = [],
				i, j, datum, row, col, output, formatterArgs, value, numericValue;
				
				ret.push(row = []);
				
				for(i=0; i<cols.length; i++){
					col = cols[i];
					output = col.output !== void 0 ? col.output : true;
					if(output){
						row.push('"' + (typeof col.name != 'undefined' ? col.name : col.id) + '"');
					}
				}
				
				for(i=0; i < (this.data.getLength ? this.data.getLength() : this.data.length); i++){
					datum = this.data.getItem ? this.data.getItem(i) : this.data[i];
					ret.push(row = []);
					for(j=0; j<cols.length; j++){
						col = cols[j];
						output = col.output !== void 0 ? col.output : true;
						if(output){
							formatterArgs = [
								i,
								j,
								(col && typeof this.options.dataItemColumnValueExtractor == 'function') ? this.options.dataItemColumnValueExtractor(datum, col) : datum[col.field],
								col,
								datum
							];
							value = col.outputFormatter ?
								col.outputFormatter.apply(this, formatterArgs) :
								col.dataFormatter ?
								col.dataFormatter.apply(this, formatterArgs) :
								(formatterArgs[2] !== null && formatterArgs[2] !== undefined ? formatterArgs[2] : '');
							numericValue = value.toString().replace(/,/g, '');
							row.push(
								!isNaN(numericValue) ? numericValue : '"' + (value ? value : '') + '"'
							);
						}
					}
				}
				
				return ret;
			},
			
			processColumns : function(cols){
				var i, col;
				for(i=0; i<cols.length; i++){
					col = cols[i];
					col.formatter = this.$class.defaultFormatter;
				}
				return cols;
			},
			
			filterHiddenColumns : function(cols){
				return $.map(cols, function(col){
					if(col.display === false) return null;
					else return col;
				});
			},
			
			checkScroll : function(){
					
				var self = this,
				slickViewport = $('.slick-viewport', this.container),
				slickPane = $('.slick-pane', this.container),
				mainPane, mainViewport, checkVScroll, checkHScroll;
				
				if(this.options.frozenColumn >= 0){
					mainViewport = slickViewport.filter('.slick-viewport-top.slick-viewport-right');
					mainPane = slickPane.filter('.slick-pane-top.slick-pane-right');
				}else{
					mainViewport = slickViewport.filter('.slick-viewport-top.slick-viewport-left');
					mainPane = slickPane.filter('.slick-pane-top.slick-pane-left');
				}
				
				(checkVScroll  = function(){
					if(!this.vSlider){
						// Create a vertical slider to scroll the table
						this.vSlider = $('<div/>')
							.appendTo(this.container)
							.css({
								'position' : 'relative',
								'float' : 'left',
								'height' : mainViewport.height() - 30,
								'marginTop' : 20,
								'marginBottom' : 10,
								'marginLeft' : 10
							})
							.slider({
								'orientation' : 'vertical',
								'min' : 0,
								'max' : 100,
								'value' : 100,
								'slide' : function(event, ui){
									(function(){
										this.scrollTop = (
											this.scrollHeight - this.offsetHeight
										) * ((100- ui.value) / 100);
									}).call(mainViewport.get(0));
								}
							});
						// Bind the scroll event of the mainViewport to the slider
						mainViewport.scroll(function(){
							self.vSlider.slider('value', 100 - (100 * this.scrollTop) /
								(this.scrollHeight - this.offsetHeight)
							);
						});	
						// Adjust the widths of the containers to account for the slider
						this.innerGridContainer.width(
							this.container.width() - this.vSlider.width() - 20
						).css('float', 'left');
						
						this.slickgrid.resizeCanvas();
					}
					else {
						this.vSlider.css('height', mainViewport.height() - 30);
					}
					if(mainViewport.get(0).scrollHeight > mainViewport.height())
						this.vSlider.slider('option', 'disabled', false);
					else
						this.vSlider.slider('option', 'disabled', true);
				}).call(this);
				// If we are in horizontal scroll territory...
				(checkHScroll = function(){
					var self = this,
					mainHeader, mainPanePosition, mainHeaderPosition;
					if(!this.hSliderTop && !this.hSliderBottom){
						// Create a horizontal slider to scroll the table
						mainHeader = slickPane.find('.slick-header').eq(0);
						mainHeaderPosition = mainHeader.position();
						mainPanePosition = mainPane.position();
						this.hSliderTop = $('<div/>')
							.appendTo(this.container)
							.css({
								'position' : 'absolute',
								'top' : mainHeaderPosition.top + mainHeader.height() - 13,
								'left' : mainPanePosition.left,
								'width' : mainViewport.width() - 28,
								'marginRight' : 30
							})
							.slider({
								'min' : 0,
								'max' : 100,
								'value' : 0,
								'slide' : function(event, ui){
									(function(){
										this.scrollLeft = (
											this.scrollWidth - this.offsetWidth
										) * (ui.value / 100);
										self.hSliderBottom.slider('value', ui.value);
									}).call(mainViewport.get(0));
								}
							})
							.addClass('hSliderTop');
						this.hSliderBottom = $('<div/>')
							.appendTo(this.container)
							.css({
								'position' : 'absolute',
								'bottom' : 0,
								'left' : mainPanePosition.left,
								'width' : mainViewport.width() - 28,
								'marginRight' : 30
							})
							.slider({
								'min' : 0,
								'max' : 100,
								'value' : 0,
								'slide' : function(event, ui){
									(function(){
										this.scrollLeft = (
											this.scrollWidth - this.offsetWidth
										) * (ui.value / 100);
										self.hSliderTop.slider('value', ui.value)
									}).call(mainViewport.get(0));
								}
							})
							.addClass('hSliderBottom');
						// Bind the scroll event of the tableScroller to the slider
						mainViewport.scroll(function(){
							self.hSliderTop.slider('value', (100 * this.scrollLeft) /
								(this.scrollWidth - this.offsetWidth)
							);
							self.hSliderBottom.slider('value', (100 * this.scrollLeft) /
								(this.scrollWidth - this.offsetWidth)
							);
						});
						// Adjust the height of the containers and vSlider to account
						// for the height of the scroller
						this.innerGridContainer.height(
							this.container.height() - this.hSliderBottom.height() - 20
						);
						// Check to see if we need an Vertical Scrollbar again
						checkVScroll.call(this);
					}
					else {
						this.hSliderTop.css('width', mainViewport.width() - 28);
						this.hSliderBottom.css('width', mainViewport.width() - 28);
					}
					
					if(mainViewport.get(0).scrollWidth > mainViewport.width()){
						this.hSliderBottom.slider('option', 'disabled', false);
						this.hSliderTop.slider('option', 'disabled', false);
						mainViewport.scroll();
					}
					else{
						mainViewport.scroll();
						this.hSliderBottom.slider('option', 'disabled', true);
						this.hSliderTop.slider('option', 'disabled', true);
						this.slickgrid.autosizeColumns();
					}
				}).call(this);
			},
			
			scrollToRight : function(){
				var slickViewport = $('.slick-viewport', this.container),
				mainViewport;
				
				if(this.options.frozenColumn >= 0){
					mainViewport = slickViewport.filter('.slick-viewport-top.slick-viewport-right');
				}else{
					mainViewport = slickViewport.filter('.slick-viewport-top.slick-viewport-left');
				}
				
				mainViewport.get(0).scrollLeft = mainViewport.get(0).scrollWidth;
			},
			
			filterNumericColumns : function(start, end){
				var i, column;
				this.filteredColumns = [];
				for(i=0; i<this.columns.length; i++){
					column = this.columns[i];
					if(!isNaN(column['id'])){
						if(column['id'] >= start && column['id'] <= end)
							this.filteredColumns.push(column);
					}else
						this.filteredColumns.push(column);
				};

				var cols = this.filterHiddenColumns(
					this.options.addSpacerColumn ? this.filteredColumns.concat([
					this.$class.spacerColumnOps	
				]) : this.filteredColumns);

				if(this.slickgrid){
					this.slickgrid.setColumns(cols);
					this.isFiltered = true;
					this.resetHSliders();
					this.checkScroll();
					if(this.options.scrollToRight) this.scrollToRight();
				}

				return cols;
			},
			
			setColumns : function(cols, dontUpdate){
				this.columns = this.processColumns(cols);
				this.isFiltered = false;
				if(this.slickgrid && !dontUpdate){
					this.slickgrid.setColumns(
						this.filterHiddenColumns(
							this.options.addSpacerColumn ? this.columns.concat([
								this.$class.spacerColumnOps
							]) : this.columns
						)
					);
					this.resetHSliders();
					this.checkScroll();
					if(this.options.scrollToRight) this.scrollToRight();
				}
			},
			
			setData : function(data){
				this.data = data;
				if(this.slickgrid){
					this.slickgrid.setData(data);
					this.resetHSliders();
					this.checkScroll();
				}
			},
			
			setOptions : function(options){
				this.options = $.extend(true, {} , this.$class.defaultOptions, options);
				if(this.slickgrid){
					this.slickgrid.setOptions(this.options);
				}
			},
			
			redraw : function(rows){
				if(rows)
					this.slickgrid.invalidateRows(rows);
				else
					this.slickgrid.invalidateAllRows();
				this.slickgrid.render();
			},
			
			resetHSliders : function(){
				this.hSliderBottom.remove();
				this.hSliderTop.remove();
				this.hSliderBottom = this.hSliderTop = null;
			},		
			
			destroy : function() {
				if(this.slickgrid) {
					this.slickgrid.destroy();
					this.slickgrid = null;
				}

				if(this.vSlider) {
					this.vSlider.slider('destroy');
					this.vSlider.remove();
					this.vSlider = null;
				}
				if(this.hSliderTop) {
					this.hSliderTop.slider('destroy');
					this.hSliderTop.remove();
					this.hSliderTop = null;
				}
				if(this.hSliderBottom) {
					this.hSliderBottom.slider('destroy');
					this.hSliderBottom.remove();
					this.hSliderBottom = null;
				}

				this.innerGridContainer.empty();
			},
			
			__classvars__ : {
				
				defaultOptions : {
					'enableCellNavigation' : false,
					'enableColumnReorder' : false
				},
				
				spacerColumnOps : {
					'id' : 'spacer',
					'name' : ' ',
					'autoWidth' : true
				},
				
				innerSlickGridHTML : '<div class="inner-slickgrid-container"></div>',
		
					
				defaultFormatter : function(row, cell, value, columnDef, dataContext){
					var data = columnDef.dataFormatter ? columnDef.dataFormatter.apply(this, arguments) : value,
					display = columnDef.displayFormatter ?
						columnDef.displayFormatter.call(this, row, cell, data, columnDef, dataContext) : data;
					
					return display;
				},
				
				autosizeColumn : function(columns, index, data, padding){
					var sandbox = $('<div/>')
					.addClass('slick-cell')
					// For proper font sizing
					.addClass('ui-widget')
					.css({
						width:'auto',
						position:'absolute',
						top:1000,
						left:0
					})
					.appendTo('body'),
					column = columns[index],
					maxWidth = 0,
					i, datum, value, widthPlusPadding;
					
					for(i=0; i < (data.getLength ? data.getLength() : data.length); i++){
						datum = data.getItem ? data.getItem(i) : data[i];
						value = datum[column.field];
						sandbox.html(
							column.formatter ?
							column.formatter(i, index, value, column, datum) :
							column.displayFormatter ?
							column.displayFormatter(i, index, value, column, datum) :
							column.dataFormatter ?
							column.dataFormatter(i, index, value, column, datum) :
							value
						);
						
						maxWidth = Math.max(maxWidth, sandbox.outerWidth());
					}

					widthPlusPadding = maxWidth + (padding != void 0 ? padding : 0);
					
					sandbox.remove();
					
					column.width = column.maxWidth !== void 0 ? Math.min(widthPlusPadding, column.maxWidth) : widthPlusPadding;
				},
				
				linkSelectorFunction: function(viewportSelector, linkSelector, nth){
					var sortedRows = {};
					var viewport = $(viewportSelector),
					viewportOffset = viewport.offset(),
					link = $(),
					i = 0,
					ith, rows, row, links;
					
					// Only sort the rows once
					if(!sortedRows[viewportSelector]){
						sortedRows[viewportSelector] = $('.slick-row', viewport).get();
						sortedRows[viewportSelector].sort(function(a,b){
							var compA = $(a).offset().top;
							var compB = $(b).offset().top;
							return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
						});
					}
					
					$.each(sortedRows[viewportSelector], function(j, ele){
						row = $(ele);
						var rowOffset = row.offset();
						if(rowOffset.top > viewportOffset.top){
							links = $(linkSelector, row);
							links.each(function(k, ele){
								link = $(ele);
								var linkOffset = link.offset();
								if(linkOffset.left > viewportOffset.left){
									if(ith === void 0)
										ith = Math.min(nth, links.length - j);
									if(i == ith)
										return false;
									else
										i++;
								}
							});
							return false;
						}else{
							return true;
						}
					});
					
					return link;
				}
			}
		});
	});
}(
	typeof define == 'function' && typeof define.amd == 'object' ? define :
	function(deps, factory){
		var i;
		for(i=0; i<deps.length; i++) deps[i] = this[deps[i]];
		this['datagrid'] = factory.apply(this, deps);
	}
);

define('js/view/MediatorAggregateDataGrid',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'datagrid'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy,
	DateCodeUtil,
	GridFormatters,
	DataGrid
){
	return puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().onClick.add(this.dataGridClick, this);
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.CHART_DATA_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.UNLOAD,
				Facade.CTYPE_PARAM_UPDATED,
				Facade.RSE_PARAM_UPDATED,
				Facade.PIN_KEY_PARAM_UPDATED
			]);
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
					timeParams = parameterProxy.getTimeParams(),
					topic = configProxy.getTopic(parameterProxy.getTopicId()),
					data = tableDataProxy.getCombinedData(),
					columns = this.generateColumns(
						tableDataProxy.getDataColumns(),
						data,
						timeParams['freq'],
						tableDataProxy.getTableUnits(),
						topic,
						parameterProxy.getShowRSE()
					);
					this.getViewComponent().update(
						data, 
						columns, 
						{
							'frozenColumn' : 2,
							'dataItemColumnValueExtractor': function(item, colDef) {
								if(colDef && colDef.field){
									return GridFormatters.nestedPropertyResolver(colDef.field, item);
								}
								else
									return null;
							},
							'scrollToRight' : true,
							'addSpacerColumn' : true
						}, 
						timeParams['start'], 
						timeParams['end']
					);
					this.sendNotification(Facade.TABLE_RENDERED);
					break;
				case Facade.TIME_PARAMS_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					if(!tableDataProxy.isUpdating){
						var timeParams = parameterProxy.getTimeParams();
						this.getViewComponent().filterNumericColumns(timeParams['start'], timeParams['end'], true);
					}
					break;
				case Facade.CHART_DATA_UPDATED:
					this.getViewComponent().redraw(note.getBody());
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'table' && note.body.type == 'csv'){
						var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
						var csvData = [
							['"' + tableDataProxy.getTableTitle() + '"'],
							['"' + window.location.href + '"'],
							['"' + Date() + '"'],
							['"Source: U.S. Energy Information Administration"']
						].concat(this.getViewComponent().outputToArrOfArrs());
						this.sendNotification(Facade.CSV_DATA_GENERATED, csvData);
					}
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				case Facade.CTYPE_PARAM_UPDATED:
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME), vc = this.getViewComponent(), columns = vc.columns,
					timeParams = parameterProxy.getTimeParams(), tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					$.each(columns, function(index, value) {
						if(value.id == 'chart') {
							value.iconClass = parameterProxy.getChartType();
						}
					});
					if(!tableDataProxy.isUpdating){
						vc.setColumns(columns);
						vc.filterNumericColumns(timeParams['start'], timeParams['end'], true);
					}
					break;
				case Facade.RSE_PARAM_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					if(!tableDataProxy.isUpdating){
						var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
						var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
						var vc = this.getViewComponent();
						var timeParams = parameterProxy.getTimeParams();
						var data = tableDataProxy.getCombinedData();
						var topic = configProxy.getTopic(parameterProxy.getTopicId());
						var columns = this.generateColumns(
							tableDataProxy.getDataColumns(),
							data,
							timeParams['freq'],
							tableDataProxy.getTableUnits(),
							topic,
							parameterProxy.getShowRSE()
						);
						vc.setColumns(columns);
					}
					break;
				case Facade.PIN_KEY_PARAM_UPDATED:
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME), vc = this.getViewComponent(), columns = vc.columns,
					timeParams = parameterProxy.getTimeParams(), tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					$.each(columns, function(index, value) {
						if(value.id == 'pinKey') {
							value.iconClass = parameterProxy.getLinkType();
						}
					});
					if(!tableDataProxy.isUpdating){
						vc.setColumns(columns);
						vc.filterNumericColumns(timeParams['start'], timeParams['end'], true);
					}
					break;
				default:
					this.$super(note);
					break;
			}
		},

		generateColumns : function(dataColumns, data, freq, units, topic, showRSE){
			var i, parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			columns = [{
				'id' : 'pinKey',
				'field' : 'HAS_DATA',
				'width' : 30,
				'displayFormatter' : GridFormatters.pinSeriesFormatter,
				'output' : false,
				'cssClass' : 'chart_column',
				'iconClass' : parameterProxy.getLinkType(),
				'resizable' : false
			},{
				'id' : 'description',
				/*'name' : units ? '(' + units + ')' : ' ',*/
				'field' : 'DESCRIPTION',
				'displayFormatter' : GridFormatters.descriptionFormatter,
				'outputFormatter' : this.$class.descriptionOutputFormatter,
				'cssClass' : 'description aggregate',
				'maxWidth' : 904,
				'width' : 400
			},{
				'id' : 'chart',
				'field' : 'HAS_DATA',
				'width' : 30,
				'displayFormatter' : GridFormatters.chartButtonFormatter,
				'output' : false,
				'cssClass' : 'chart_column',
				'iconClass' : parameterProxy.getChartType(),
				'resizable' : false
			},
			{
				'id' : 'units',
				'name' : 'units',
				'field' : 'UNITS',
				'display' : false
			},
			{
				'id' : 'source key',
				'name' : 'source key',
				'field' : 'SERIES_ID',
				'display' : false
			}];
			
			DataGrid.autosizeColumn(columns, 0, data, 5);
			
			for(i = 0; i < dataColumns.length; i++){
				var columnKey = parseInt(dataColumns[i]).toString();
				columns.push({
					'id' : columnKey,
					'name' : DateCodeUtil.formatDateCode(columnKey, freq),
					'field' : 'DATA.' + columnKey.toString(),
					'rseField' : 'RSE_DATA.' + columnKey.toString(),
					'drillDownTopicId' : topic.drillDownTopicId,
					'displayFormatter' : topic.drillDownTopicId ? this.$class.linkCellFormatter : null,
					'showRSE' : showRSE,
					'dataFormatter' : GridFormatters.numericalDataFormatter,
					'freq' : freq,
					'width' : showRSE ? 150 : 100
				})
			}
			
			return columns;
		},
			
		dataGridClick : function(row, cell, datum){
			var vc = this.getViewComponent(), columns = vc.columns;
			var chartType = columns[cell].iconClass;
			if(columns[cell].id == 'chart' || columns[cell].id == 'pinKey'){
				if(datum.ACTION){
					var chartedObj = {};
					chartedObj[chartType] = '';
					
					this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
				}
				else{
					switch(chartType) {
						case 'linechart' : 
						case 'columnchart' :
						case 'pin':
							var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
							parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
							if(datum['HAS_DATA']) {
								var chartedObj  = {};
								chartedObj[chartType] = parameterProxy.getUpdatedCharted(datum.SERIES_ID, chartType);
								this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
							}
							else {
								var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
								children = tableDataProxy.getChildData(row),
								childrenIds = $.map(children, function(value){
									return value.HAS_DATA ? value.SERIES_ID : null;
								});
								
								if(childrenIds.length > 0) {
									var chartedObj = {};
									chartedObj[chartType] = parameterProxy.getUpdatedCharted(childrenIds, chartType, Boolean(datum[chartType]));
									this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
								}
							}
							break;
						case 'map' :
								var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
								chartType = parameterProxy.getChartType();
								if(datum.HAS_DATA && datum.GEOSET_ID != null){
									chartedObj = {};
									chartedObj[chartType] = datum.SERIES_ID;
									this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
								}
							break;
						case 'sourcekey' :
							if(datum.SERIES_ID && datum['HAS_DATA']){
								this.sendNotification(Facade.SOURCE_KEY_SELECTED, datum.SERIES_ID);
							}
							break;
					}
				}
			}
			else if(cell > 2){
				var columnDef = this.getViewComponent().columns[cell + 2];
				if(columnDef && datum.HAS_DATA && datum.filterQuery && columnDef.drillDownTopicId){
					var label = 'topic/'+ columnDef.drillDownTopicId + '?agg=' + datum.agg + '&' + datum.filterQuery + '&freq=' + columnDef.freq + '&datecode=' + columnDef.id;
					
					var filterParams = function(q){
						var ret = {};
						var pairs = $.map(q.split('&'), function(p){
							return [p.split('=')];
						});
						$.each(pairs, function(i, p){
							ret[p[0]] = p[1];
						});
						return ret;
					}(datum.filterQuery);
					
					var body = {
						topic : columnDef.drillDownTopicId,
						params : $.extend({
							agg : datum.agg,
							freq : columnDef.freq,
							datecode : columnDef.id
						}, filterParams),
						label : label
					};
					
					this.sendNotification(Facade.AGGREGATE_DATA_CLICK_THROUGH, body, 'AggregateClickThrough');
				}
			}
		},
		
		__classvars__ : {
			
			NAME : 'DataGridMediator',
			
			linkCellFormatter : function(row, cell, value, columnDef, datum){
				return value !== '' && datum.filterQuery ? '<span class="link_cell">' + value + '</span>' : value;
			},
			
			descriptionOutputFormatter : function(row, cell, value, columnDef, datum){
				return datum['CHART_NAME'] || value;
			}
		}
	});
});
define('js/view/MediatorStateCoalBasinSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent, top){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().selectionChanged.add(function(rtype){
				self.sendNotification(Facade.REGION_TYPE_SELECTION_UPDATED, {'rtype' : rtype}, 'RegionTypeChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.REGION_TYPE_PARAM_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var vc = this.getViewComponent();
			
			switch(note.getName()){
				case Facade.REGION_TYPE_PARAM_UPDATED:
					if(vc.stateCoalBasinSelect == null) {
						vc.create(parameterProxy.getRegionType());
					}
					else {
						vc.updateSelection(parameterProxy.getRegionType());
					}
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'StateCoalBasinSelectionMediator'
		}
	});
});

define('text!templates/main.html',[],function () { return '<span id="top_topic_selection_container" class="grey_side ltblue"></span>\r\n<div class="carousel blue_top" id="chart_area"></div>\r\n<hr />\r\n<div id="middle_topic_selection_container" class="grey_side ltblue"></div>\r\n<div id="title_container"></div>\r\n{{#mainDownloadButtonOpts}}\r\n\t{{>download_button}}\r\n{{/mainDownloadButtonOpts}}\r\n<div id="help_button"\tclass="help_button">\r\n\t<span class="help_icon"> </span><span class="help_button_text">Help</span>\r\n</div>\r\n<div id="import_export_container"></div>\r\n{{#mainFilterButtonOpts}}\r\n\t{{>filter_button}}\r\n{{/mainFilterButtonOpts}}\r\n<div id="mine_plant_container"></div>\r\n<div id="region_type_selection_container"></div>\r\n{{#mainAnalysisButtonOpts}}\r\n\t{{>analysis_button}}\r\n{{/mainAnalysisButtonOpts}}\r\n{{#mainVisualizationButtonOpts}}\r\n\t{{>visualization_button}}\r\n{{/mainVisualizationButtonOpts}}\r\n<div class="clear"></div>\r\n<div id="search_time_container">\r\n\t<div id="search_container"></div>\r\n\t<div id="pinkey_container"></div>\r\n\t<div id="time_selection_container"></div>\r\n</div>\r\n<div class="clear"></div>\r\n<div id="tableContainer">\r\n</div>\r\n<div id="footnote_container">\r\n\t\r\n</div>\r\n<div class="clear"></div>\r\n<div id="filter_dialog_wrap">\r\n\t<div id="filter_dialog_container">\r\n\t\t{{#overlayFilterButtonOpts}}\r\n\t\t\t{{>filter_button}}\r\n\t\t{{/overlayFilterButtonOpts}}\r\n\t\t<div id="filter_dialog"></div>\r\n\t</div>\r\n</div>\r\n<div id="download_dialog_wrap">\r\n\t<div id="download_dialog_container">\r\n\t\t\t{{#overlayDownloadButtonOpts}}\r\n\t\t\t\t{{>download_button}}\r\n\t\t\t{{/overlayDownloadButtonOpts}}\r\n\t\t<div id="download_dialog"></div>\r\n\t</div>\r\n</div>\r\n<div id="analysis_dialog_wrap">\r\n\t<div id="analysis_dialog_container">\r\n\t\t\t{{#overlayAnalysisButtonOpts}}\r\n\t\t\t\t{{>analysis_button}}\r\n\t\t\t{{/overlayAnalysisButtonOpts}}\r\n\t\t<div id="analysis_dialog"></div>\r\n\t</div>\r\n</div>';});


define('text!templates/filter_button.html',[],function () { return '<div\r\n\t{{#id}}\r\n\t\tid="{{id}}"\r\n\t{{/id}}\r\n\tclass="filter_button\r\n\t\t{{#selected}}\r\n\t\t\tselected\r\n\t\t{{/selected}}\r\n\t\t{{#disabled}}\r\n\t\t\tdisabled\r\n\t\t{{/disabled}}\r\n\t\t\r\n\t"\r\n>\r\n\t<span class="filter_icon"> </span><span class="filter_button_text">Filter/order</span>\r\n</div>';});


define('text!templates/download_button.html',[],function () { return '<div\r\n\t{{#id}}\r\n\t\tid="{{id}}"\r\n\t{{/id}}\r\n\tclass="download_button\r\n\t\t{{#selected}}\r\n\t\t\tselected\r\n\t\t{{/selected}}\r\n\t"\r\n>\r\n\t<span class="download_icon"> </span><span class="download_button_text">Download</span>\r\n</div>';});


define('text!templates/visualization_button.html',[],function () { return '<div\r\n\t{{#id}}\r\n\t\tid="{{id}}"\r\n\t{{/id}}\r\n\tclass="\r\n\t\t{{#disabled}}\r\n\t\t\tdisabled\r\n\t\t{{/disabled}}\r\n\t\t\r\n\t"\r\n>\r\n</div>';});


define('text!templates/analysis_button.html',[],function () { return '<div\r\n\t{{#id}}\r\n\t\tid="{{id}}"\r\n\t{{/id}}\r\n\tclass="analysis_button\r\n\t\t{{#selected}}\r\n\t\t\tselected\r\n\t\t{{/selected}}\r\n\t\t{{#disabled}}\r\n\t\t\tdisabled\r\n\t\t{{/disabled}}\r\n\t\t\r\n\t"\r\n>\r\n\t<span class="analysis_icon"> </span><span class="analysis_button_text">Analysis</span>\r\n</div>';});

define('js/view/VC_PageLayout',[
	'classy',
	// Templates
	'text!templates/main.html',
	'text!templates/filter_button.html',
	'text!templates/download_button.html',
	'text!templates/visualization_button.html',
	'text!templates/analysis_button.html'
], function(
	Class,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	visualizationsButtonTemplate,
	analysisButtonTemplate
){
	return Class.$extend({
		
		container : null,
		
		__init__ : function(container, options){
			this.container = container;
			
			this.container.empty().html(
				Mustache.render(pageTemplate, options, {
					'filter_button' : filterButtonTemplate,
					'download_button' : downloadButtonTemplate,
					'visualization_button' : visualizationsButtonTemplate,
					'analysis_button' : analysisButtonTemplate
				})
			);
		},
		
		getFormContainer : function(){
			return $('#filter_dialog', this.container);
		},
		
		getVisualizationContainer : function() {
			return $('#main_visualization_button', this.container);
		},
		
		getPinKeyVisualizationContainer : function() {
			return $('#pinkey_container', this.container);
		},
		
		getDownloadContainer : function(){
			return $('#download_dialog', this.container);
		},
		
		getTopTopicSelectionContainer : function(){
			return $('#top_topic_selection_container', this.container);
		},

		getMiddleTopicSelectionContainer : function(){
			return $('#middle_topic_selection_container', this.container);
		},
		
		getTimeSelectionContainer : function(){
			return $('#time_selection_container', this.container);
		},
		
		getSearchContainer : function(){
			return $('#search_container', this.container);
		},
		
		getTitleContainer : function(){
			return $('#title_container', this.container);
		},
		
		getTableContainer : function(){
			return $('#tableContainer', this.container);
		},
		
		getChartContainer : function(){
			return $('#chart_area', this.container);
		},
		
		getFootnoteContainer : function(){
			return $('#footnote_container', this.container);
		},
		
		getAnalysisContainer : function(){
			return $('#analysis_dialog', this.container);
		},
		
		getImportExportSelectionContainer : function() {
			return $('#import_export_container', this.container);
		},
		
		getRegionTypeSelectionContainer : function() {
			return $('#region_type_selection_container', this.container);
		},

		getMinePlantSelectionContainer : function() {
			return $('#mine_plant_container', this.container);
		}
	})
});

define('text!templates/filterBox.html',[],function () { return '<div class="style2">\r\n\t<div class="filter_buttons">\r\n\t\t<span id="cancelButton"><span>Cancel</span><div> </div></span>\r\n\t\t<!--<span id="resetButton"><span>Clear</span><div> </div></span>-->\r\n\t\t<span id="submitButton"><span>Submit</span><div> </div></span>\r\n\t</div>\r\n\t<ul class="filter_menu">\r\n\t{{#aggColumns}}\r\n\t<li class="filter_menu_item" id="aggId={{aggColumnId}}">\r\n\t\t<div class="filter_menu_header">\r\n\t\t\t<h3></h3>\r\n\t\t</div>\r\n\t\t<div class="filter_menu_title">\r\n\t\t\t<span class="arrow left"> </span>\r\n\t\t\t<span class="title_text">{{description}}</span>\r\n\t\t\t<span class="arrow right"> </span>\r\n\t\t</div>\r\n\t\t<div class="filter_tree_div">\r\n\t\t\r\n\t\t\t<div class="filter_all">\r\n\t\t\t\t<span><a class="check">Check all</a></span>\r\n\t\t\t\t<span>  |  </span>\r\n\t\t\t\t<span><a class="uncheck">Uncheck all</a></span>\r\n\t\t\t</div>\r\n\t\t\t<div class="filter_tree_scroller"/>\r\n\t\t\t<ul class="filter_tree" id="{{filterGroup.varName}}">\r\n\t\t\t\t<form>\r\n\t\t\t\t{{{filterTreeFunction}}}\r\n\t\t\t\t</form>\r\n\t\t\t</ul>\r\n\t\t</div>\r\n\t</li>\r\n\t{{/aggColumns}}\r\n\t<li class="clear"/>\r\n\t</ul>\r\n</div>';});


define('text!templates/selectionItem.html',[],function () { return '<input type="checkbox" value="{{valueId}}" name="{{filterGroupId}}"/><p>{{description}}</p>';});

define('js/view/VC_Form',[
	'classy',
	'text!templates/filterBox.html',
	'text!templates/selectionItem.html',
	'signals',
	'mousewheel',
	'mustache',
	'simpleModal'
],
function(Class, filterBoxTemplate, selectionItemTemplate, signals)
{
	var setFilterMenuHeaders = function() {	
		$.each($('.filter_menu_header > h3'),function(index, value) {
			if(index == 0 && index < $('.filter_menu_header > h3').length - 2) {
				$(value).html("Order by <span class='note'>(choose one)</span>");
			}
			else if(index < $('.filter_menu_header > h3').length - 2) {
				$(value).html("Then by <span class='note'>(choose one)</span>");
			
			}
			else if(index == 0) {
				$(value).html("Order by <span class='note'>(choose one or more)</span>");
			}
			else{
				$(value).html("Then by <span class='note'>(choose one or more)</span>");
			}
		});
	};
	
	var setArrowStates = function() {
		var arrows = $('.filter_menu_title > span:not(.title_text)');
		
		arrows.addClass('arrow');
		
		$(arrows.get(0)).toggleClass('arrow');
		$(arrows.get(arrows.length-1)).toggleClass('arrow');
	};
	
	var setCheckBoxesRadios = function() {
		var filterMenuItems = $('.filter_menu_item');
		
		$.each(filterMenuItems, function(index) {
			if(index < filterMenuItems.length-2)
			{
				var addedChecked = false;
				$(this).find('input[type="checkbox"]').each(function(index) {
					var parent = $(this).parent();
					var newCtl = $('<input type="radio" value="' + $(this).attr('value') + '" name="' + $(this).attr('name') + '"/>');
					if($(this).is(':checked') && !addedChecked) { 
						newCtl.attr('checked', true);
						addedChecked = true;
					}
					parent.prepend(newCtl);
					$(this).remove();
				});
				$(this).find('.filter_all').css('visibility', 'hidden');
			}
			else
			{
				$(this).find('input[type="radio"]').each(function(index) {
					var parent = $(this).parent();
					var newCtl = $('<input type="checkbox" value="' + $(this).attr('value') + '" name="' + $(this).attr('name') + '"/>');
					if($(this).is(':checked')) { newCtl.attr('checked', true);}
					parent.prepend(newCtl);
					$(this).remove();
				});
				$(this).find('.filter_all').css('visibility', 'visible');
			}
		});
	};
	
	var arrowClickHandler = function(event) {
		var filterMenuItem = $(event.target).parents('.filter_menu_item');
		
		if($(this).hasClass('left')) {
			filterMenuItem.insertBefore(filterMenuItem.prev());
		}
		else {
			filterMenuItem.insertAfter(filterMenuItem.next());
		}
		
		setArrowStates();
			
		setFilterMenuHeaders();
		
		setCheckBoxesRadios();
	};
	
	var sortableUpdate = function(event, ui) {
		setArrowStates();
			
		setFilterMenuHeaders();
		
		setCheckBoxesRadios();
		
	};
	
	var sortableStop = function(event, ui) {
		var scroller = ui.item.find('.filter_tree_scroller');
		scroller.slider('option', 'slide').call(scroller, null, {handle: scroller.find('a'), value: scroller.slider('value')});
	};
	
	var mouseWheelHandler = function(event, delta, deltaX, deltaY) {
		var scrollerEle = this,
		yDiff = 1, xDiff = 1;
		if (deltaY){
			var top = parseInt(scrollerEle.scrollTop, 10) + (deltaY > 0 ? -120 : 120);
			//if (top > 0) top = 0;
			scrollerEle.scrollTop = top;
			yDiff = (scrollerEle.scrollHeight - scrollerEle.offsetHeight) - top;
		}
		if(deltaX){
			var left = parseInt(scrollerEle.scrollLeft, 10) + (deltaX < 0 ? -120 : 120);
			//if (top > 0) top = 0;
			scrollerEle.scrollLeft = left;
			xDiff = (scrollerEle.scrollWidth - scrollerEle.offsetWidth) - left;
		}
		return false;
	};
	
	var plusMinusClick = function() {
		$(this).toggleClass('disabled');
		$(this).parent().parent().toggleClass('disabled');
	};
	
	var filterTreeFunction = function(text) {
		var ret = "";
		var filterValues = this.filterGroup.filterValues;
		var previousLevel = 0, currentLevel = 0, nextLevel = 0;
		
		for(var i = 0; i < filterValues.length; i++) {
			currentLevel = filterValues[i].level;
			nextLevel = (i < (filterValues.length - 1)) ? filterValues[i + 1].level : 0;
			
			if(currentLevel < previousLevel) {
				for(var j = 0; j < previousLevel - currentLevel; j++) {
					ret += '</ul></li>';
				}
			}
			else if(currentLevel > previousLevel) {
				ret += '<ul>';
			}
			var txt = {
				description: filterValues[i].description,
				valueId : filterValues[i].valueId,
				filterGroupId: filterValues[i].filterGroupId
			};
			txt.description = txt.description.charAt(0).toUpperCase() + txt.description.slice(1);
			ret += '<li>' + Mustache.to_html(selectionItemTemplate, txt) + ((nextLevel <= currentLevel) ? '</li>' : '');
			
			previousLevel = currentLevel;
		}
		return ret;
	};
	
	var submitButtonHandler = function(event) {
		var subObj = buildSubmitObject();
		event.data.dispatch(buildSubmitObject());
	};
	
	var cancelButtonHandler = function(event) {
		event.data.dispatch();
	};
	
	var resetButtonHandler = function(event) {
		$('.filter_tree').find('input:checked').attr('checked', false);
		event.data.dispatch();
	};
	
	var checkUncheckHandler = function(event) {
		$(event.delegateTarget).parent().find('input').attr('checked', $(event.target).hasClass('check'));
	};
	
	var buildSubmitObject = function() {
		var ret = {};
		
		// Build agg variable
		var agg = "";
		var singleMultiSelects = "", multiMultiSelects = "";
		$.each($('.filter_menu_item'), function(index) {
			if($('input', this).attr('type').toLowerCase() == 'radio') {
				agg += this.id.substr(this.id.indexOf('=') + 1) + ',';
			}
			else if($(':checked', this).length > 1) {
				multiMultiSelects += this.id.substr(this.id.indexOf('=') + 1) + ',';
			}
			else {
				singleMultiSelects += this.id.substr(this.id.indexOf('=') + 1) + ',';
			}
		});
		agg = agg + singleMultiSelects + multiMultiSelects;
		
		ret.agg = agg.substr(0, agg.length-1);
		
		// Build filters from checkboxes
		$('.filter_tree').each(function() {
			var controls = $(this).find(':checked');
			ret[this.id] = "1";
			var checkedArray = [];
			controls.each(function() {
				checkedArray[$(this).attr('value')] = 1;
			});
			
			var binaryString = "";
			
			ret[this.id] = "";
			
			for(var i = 0; i < Math.ceil(checkedArray.length / 5) * 5; i++) {
				if(checkedArray[i] && checkedArray[i] == 1) {
					binaryString += "1";
				}
				else {
					binaryString += "0";
				}
				if((i % 5) == 4) {
					ret[this.id] = ret[this.id] + parseInt(binaryString, 2).toString(32);
					binaryString = "";
				}
			}
			ret[this.id] = (ret[this.id] == "") ? "g" : ret[this.id];
			
		});
		
		return ret;
	};
	
	var updateFormSelections = function(hashData) {
		var filterMenuItems = $('.filter_menu_item');
		
		if(hashData.agg != null){
			var aggArray = hashData.agg.toString().split(',');
			
			for(var i = 0; i < aggArray.length ; i++) {
				var curr = $('.filter_menu_item[id$=\\=' + aggArray[i] + ']');
				/*for(var j = 0; j < filterMenuItems.length; j++) {
					var curr = filterMenuItems.get(j);
					if(aggArray[i] == curr.id.substr(curr.id.indexOf('=') + 1)) {*/
						$(curr).insertBefore($(curr).parent().find('>.clear'));
					/*}
				}*/
			}
		}
		
		
		setFilterMenuHeaders();
		setArrowStates();
		setCheckBoxesRadios();
		
		filterMenuItems.each(function() {
			var filterMenuItem = this;
			var varName = $(filterMenuItem).find('.filter_tree').get(0).id;
			var controls = $(filterMenuItem).find('input');
			
			if(hashData[varName] != null) {
				// Create binary string and pad the binary string so it is always of length 5
				var binaryString = '';//parseInt(hashData[varName], 32).toString(2);
				$.each(hashData[varName].toString().split(''), function(index, value) {
					var partBinaryString = parseInt(value.toString(),32).toString(2);
					while((partBinaryString.length % 5) != 0) { partBinaryString = '0' + partBinaryString;}
					binaryString +=  partBinaryString;
				});
				
				
				$.each(controls, function(index) { $(this).attr('checked', false);});
				
				if(controls.length > 0 && $(controls[0]).attr('type') == 'radio') {
					var control = $(filterMenuItem).find('input[value='+binaryString.indexOf('1')+']');
					if(control.length > 0) { 
						$(control[0]).attr('checked', true);
					}
				}
				else {
					$.each(binaryString.split(''), function(index, value) {
						if(value == 1) {
							var control = $(filterMenuItem).find('input[value='+index+']');
							if(control.length > 0) {
								$(control[0]).attr('checked', true);
							}
						}
					});
				}
			}
			var checked = $(filterMenuItem).find('input:checked');
			if(checked.length == 0 && controls.length > 0)
			{
				$(controls[0]).attr('checked', true);
			}
		});
	};
	
	return 	Class.$extend({
		
		form : null,
		button : null,
		
		formSubmitted : null,
		formCanceled : null, 
		formButtonClicked : null,
		formReset : null,
		
		resizeCallback : null,
		
		opened : false,
		
		__init__ : function(container){
			var self = this;
			
			this.container = container;
			this.button = $('#main_filter_button');
			this.formSubmitted = new signals.Signal();
			this.formCanceled = new signals.Signal();
			this.formReset = new signals.Signal();
			this.formButtonClicked = new signals.Signal();
			
			this.button.click( function(e){
				if(!self.button.hasClass('disabled'))
					self.formButtonClicked.dispatch();
			});
		},
		
		generateForm : function(formContainer, formOptions, hashData){
			//add class to adjust width based on number of aggColumns
			switch(formOptions.aggColumns.length) {
				case 1:
					$(this.container).addClass('oneWide');
					break;
				case 2:
					$(this.container).addClass('twoWide');
					break;
				case 3:
					$(this.container).addClass('threeWide');
					break;
				case 4:
					$(this.container).addClass('fourWide');
					break;
			}
			
			// Add filterTreeFunction to each aggColumn for use by Mustache.js
			for(var i = 0; i < formOptions.aggColumns.length; i++) {
				formOptions.aggColumns[i].filterTreeFunction = filterTreeFunction;
			}
			
			// Generate filterMenu object within mustache
			var filterMenu = $(Mustache.to_html(filterBoxTemplate, formOptions));
			
			formContainer.empty();
			formContainer.append(filterMenu);
			
			// Turn each filter_tree in the generated filterMenu into a treeview object
			// Set the scroll function and bind the mouseWheelHandler
			filterMenu.find('.filter_tree').bind('scroll', function() {
				$(this).siblings('.filter_tree_scroller').slider('value', ((this.scrollHeight - this.offsetHeight) == 0) ? 100 :(100 - (100 * this.scrollTop) / (this.scrollHeight - this.offsetHeight)));
			})
			.bind('mousewheel', mouseWheelHandler);
			
			//Sorting interaction event and set each arrowState
			filterMenu.find('.arrow').bind('click', arrowClickHandler);

			// Attach event to submit button
			filterMenu.find('#submitButton').bind('click', this.formSubmitted, submitButtonHandler);
			filterMenu.find('#cancelButton').bind('click', this.formCanceled, cancelButtonHandler);
			filterMenu.find('#resetButton').bind('click', this.formReset, resetButtonHandler);
			
			//Attach check/uncheck all events
			filterMenu.find('.filter_all').on('click', 'a', checkUncheckHandler);
			
			updateFormSelections(hashData);
		},
		
		showForm : function(config, formParams){
			var buttonOffset = this.button.offset(),
			self = this;
			
			this.generateForm(
				this.container,
				config,
				formParams
			);
			
			var filterDialogContainer = $('#filter_dialog_container');
			
			var origParent = filterDialogContainer.parent();
			
			filterDialogContainer
				.appendTo('body')
				.css({
					position : 'absolute',
					display: 'block',
					left: -9999,
					top: -9999
				});
				
			var containerHeight = filterDialogContainer.height();
			
			filterDialogContainer
				.appendTo(origParent)
				.css({
					position: 'relative',
					left:0,
					top:0
				});

			filterDialogContainer.modal({
				'autoPosition' : false,
				'overlayClose' : true,
				'fixed' : false,
				'persist' : true,
				'minHeight' : containerHeight,
				'maxHeight' : containerHeight,
				'onShow' : function() { 
					// Turn each filter_tree_scroller into a jquery ui slider
					
					$('.filter_tree_scroller').each(function(){
						ele = $(this);
						ele.slider({
							'orientation' : 'vertical',
							'min' : 0,
							'max' : 100,
							'value' : 100,
							'slide' : function(event, ui){
								(function(){
									this.scrollTop = (
										this.scrollHeight - this.offsetHeight
									) * ((100- ui.value) / 100);
								}).call($(ui.handle).parent().siblings('.filter_tree').get(0));
							}
						}) //fix for mousedown on the slider propogating up to the sortable component in IE
						.mousedown(function(e) {
							if($.browser.msie) {
								e.stopPropagation();
							}
						});
						var scrollArea = ele.siblings('.filter_tree');
						if(scrollArea.innerHeight() >= scrollArea.get(0).scrollHeight){
							ele.slider('option', 'disabled', true);
						}
					})
					
					$('.filter_menu').sortable({
						update: sortableUpdate,
						items: '.filter_menu_item',
						axis : 'x',
						stop: sortableStop
					});
				},
				'close' : false
			});
			
			$('#simplemodal-container').css({
				'top' : buttonOffset.top,
				'left' : buttonOffset.left
			});
			
			$(window).resize(this.resizeCallback = function(){
				var buttonOffset = self.button.offset();
				$('#simplemodal-container').css({
					'top' : buttonOffset.top,
					'left' : buttonOffset.left
				})
			});
			
			window.scrollTo(0, this.button.offset().top);
			
			this.opened = true;
		},
		
		hideForm : function(){
			this.container.empty();
			if($(this.container).hasClass('oneWide')) {
				$(this.container).removeClass('oneWide');
			}
			else if($(this.container).hasClass('twoWide')) {
				$(this.container).removeClass('twoWide');
			}
			else if($(this.container).hasClass('threeWide')) {
				$(this.container).removeClass('threeWide');
			}
			else if($(this.container).hasClass('fourWide')) {
				$(this.container).removeClass('fourWide');
			}
			$.modal.close();
			if(typeof this.resizeCallback == 'function') {
				$(window).unbind('resize', this.resizeCallback);
			}
			this.resizeCallback = null;
			this.opened = false;
		}
	});
});

define('text!templates/downloadBox.html',[],function () { return '<div class="style2">\r\n\t\t<dl class="download_menu">\r\n\t\t\t{{#downloadItems}}\r\n\t\t\t<dt>{{#id}}<a id="{{id}}" class="download_option {{class}}">{{/id}}{{label}}{{#id}}</a>{{/id}}</dt>\r\n\t\t\t{{#children}}\r\n\t\t\t\t<dd><a id="{{id}}" class="download_option {{class}}">{{label}}</a></dd>\r\n\t\t\t{{/children}}\r\n\t\t\t{{/downloadItems}}\r\n\t\t\t<dt class="clear"/>\r\n\t\t</dl>\r\n</div>';});

define('js/view/VC_Download',[
	'classy',
	'text!templates/downloadBox.html',
	'signals',
	'mustache',
	'simpleModal'
],
function(Class, downloadBoxTemplate, signals)
{
	return 	Class.$extend({
		
		__init__ : function(container){
			var self = this;
			
			this.container = container;
			this.button = $('#main_download_button');
			this.downloadButtonClicked = new signals.Signal();
			this.downloadOptionClicked = new signals.Signal();
			
			this.button.click( function(e){
				if(!self.button.hasClass('disabled'))
					self.downloadButtonClicked.dispatch();
			});
			
			this.container.delegate('.download_option', 'click', function(event){
				self.hideDownload();
				self.downloadOptionClicked.dispatch(self.idMap[$(event.target).attr('id')]);
			});
			
			this.modalCloseHandler = function(e){
				if($(e.target).parents('#download_dialog_container').length == 0){
					self.hideDownload();
				}
			};
		},
		
		generateDownload : function(downloadContainer, downloadItems){
			
			this.idMap = function(){
				var ret = {};
				for(var i = 0; i < downloadItems.length; i++){
					var item = downloadItems[i];
					if(item.id)
						ret[item.id] = item;
					else if(downloadItems[i].children){
						for(var j=0; j<item.children.length; j++){
							ret[item.children[j].id] = item.children[j];
						}
					}
				}
				return ret;
			}();
			
			downloadContainer.empty();
			
			// Generate filterMenu object within mustache
			var downloadMenu = $(Mustache.to_html(downloadBoxTemplate,{downloadItems : downloadItems}));
			
			downloadContainer.append(downloadMenu);
		},

		showDownload : function(config){
			
			var self = this;
			var buttonOffset = self.button.offset();

			this.generateDownload(
				this.container,
				config
			);

			$('#download_dialog_container').modal({
				'autoPosition' : false,
				'overlayClose' : true,
				'fixed' : false,
				'close' : false,
				'persist' : true
			});
			
			$('#simplemodal-container').css({
				'top' : buttonOffset.top,
				'left' : buttonOffset.left
			});
			
			// Assuming this is called in response to a mouse click on a
			// element other than the body, then the current click event
			// will bubble up to the body, so we attach the actual click
			// handler then.
			$('body').one('click', function(){
				$('body').bind('click', self.modalCloseHandler);
			});
			
			this.container.css({
				left : this.button.outerWidth() - this.container.outerWidth()
			});
			
			$(window).resize(this.resizeCallback = function(){
				var buttonOffset = self.button.offset();
				$('#simplemodal-container').css({
					'top' : buttonOffset.top,
					'left' : buttonOffset.left
				})
			});
		},
		
		hideDownload : function(){
			$.modal.close();
			if(typeof this.resizeCallback == 'function') {
				$(window).unbind('resize', this.resizeCallback);
			}
			if(typeof this.modalCloseHandler == 'function') {
				$('body').unbind('click', this.modalCloseHandler);
			}
			this.resizeCallback = null;
		}
	});
});

define('text!templates/visualizationBox.html',[],function () { return '{{#items}}\r\n<input type="radio" id="{{class}}-{{rtype}}-radio" name="{{name}}" value="{{class}}"/><label for="{{class}}-{{rtype}}-radio" alt="{{description}}" title="{{description}}" ><span class="ico {{class}}"></span>{{label}}</label>\r\n{{/items}}';});

define('js/view/VC_Visualization',[
	'classy',
	'text!templates/visualizationBox.html',
	'signals',
	'mustache'
],
function(Class, boxTemplate, signals)
{
	return 	Class.$extend({		
		__init__ : function(container, config){
			var self = this;

			this.visualizationSelected = new signals.Signal();

			this.container = container;
			this.config = $.extend({}, (config ? config : this.$class.defaultConfig));

			this.generateMenu(
				this.container,
				this.config
			);

			this.container.on('change', 'input', function(event) {
				self.updateSelection(event.target);
				var item = self.config.items[self.currentSelectionIndex];
				self.visualizationSelected.dispatch({
					'newClass': item['class'],
					'item': item, 
					'event' : event
				});
			});
		},
		
		generateMenu: function(container, options, hashData) {
			this.config = $.extend(this.config, options);

			this.items = this.config.items;
			var menu = $(Mustache.to_html(boxTemplate, {items : this.items}));
			
			container.empty().append(menu);

			$(this.container).buttonset();
			this.visualizationSelectors = $('input', this.container);

			this.updateSelectionIndex(this.config.currentSelectionIndex ? this.config.currentSelectionIndex : 0);
		},
		
		updateSelection : function(element) {
			var self = this;

			for(var i = 0; i < this.visualizationSelectors.length; i++) {
				if(this.visualizationSelectors[i] == element) {
					self.updateSelectionIndex(i);
					break;
				}
			}
		},
		
		updateSelectionIndex: function(index) {
			var self = this;
			this.currentSelectionIndex = index;

			$(this.visualizationSelectors[index]).attr('checked', true);

			$(this.container).buttonset('refresh');
		},

		__classvars__ : {
			defaultConfig : {
				items : [
					{
						'name' : 'visualizations',
						'class' : 'linechart',
						'description' : 'Time-series chart',
						'label' : 'Time-series'
					},
					{
						'name' : 'visualizations',
						'class' : 'columnchart',
						'description' : 'Column chart',
						'label' : 'Column'
					},
					{
						'name' : 'visualizations',
						'class' : 'map',
						'description' : 'U.S. map over time',
						'label' : 'U.S. map',
						'rtype' : 's'
					}
				],
				currentSelectionIndex : 0
			},
			pinKeyConfig : {
				items : [
					{
						'name' : 'pinKeyVisualizations',
						'class' : 'pin',
						'description' : 'Pin Series for comparison across data sets',
						'label' : 'Pin'
					},
					{
						'name' : 'pinKeyVisualizations',
						'class' : 'sourcekey',
						'description' : 'Link to API source key',
						'label' : 'API'
					}
				],
				currentSelectionIndex : 0
			}
		}
	});
});

define('text!templates/topic_selector.html',[],function () { return '<div class="topic_selection_inner_container">\r\n\t<h1>Change data set</h1>\r\n\t\t<form id="data_set" name="data_set">\r\n\t\t\t<!---label id="topic_select_label" for="topic_select">Change Data Set: </label--->\r\n\t\t\t<select id="topic_select" size="1" name="topic_select" class="link_prompt">\r\n\t\t\t\t{{#topics}}\r\n\t\t\t\t{{#label}}<optgroup label="{{label}}">\r\n\t\t\t\t{{#topics}}\r\n\t\t\t\t<option class="link_option" value="{{dataTopicId}}">{{description}}</option>\r\n\t\t\t\t{{/topics}}\r\n\t\t\t\t</optgroup>\r\n\t\t\t\t{{/label}}\r\n\t\t\t\t{{^label}}\r\n\t\t\t\t<option class="link_option" value="{{dataTopicId}}">{{description}}</option>\r\n\t\t\t\t{{/label}}\r\n\t\t\t\t{{/topics}}\r\n\t\t\t</select>\r\n\t\t</form>\r\n</div>\r\n<div class="topic_selection_inner_container or">\r\n\t<h1>OR</h1>\r\n</div>\r\n<div class="topic_selection_inner_container">\r\n\t<h1>View a pre-generated report</h1>\r\n\t\t<form id="choose_report" name="choose_report">\r\n\t\t\t<select class="link_prompt" id="report_select" size="1" name="report_select">\r\n\t\t\t\t<option value="">Choose a report</option>\r\n\t\t\t\t{{#reports}}\r\n\t\t\t\t<option class="link_option" value="{{json}}">{{description}}</option>\r\n\t\t\t\t{{/reports}}\r\n\t\t\t</select>\r\n\t\t</form>\r\n</div>\r\n<div class="clear"></div>';});

define('js/view/VC_Selection',[
	'classy',
	'signals',
	'text!templates/topic_selector.html',
	'json2'
],
function(Class, signals, topicSelectorTemplate)
{
	return Class.$extend({
		
		topicSelect : null,
		reportSelect : null,
		
		topicSelectionChanged : null,
		reportSelectionChanged : null,
		
		__init__ : function(container, top){
			
			this.top = top;
			this.container = container;
			this.topicSelectionChanged = new signals.Signal();
			this.reportSelectionChanged = new signals.Signal();
		},
		
		create : function(topicId, reportIndex, topics, reports){
			var self = this;
			
			this.container.html(
				Mustache.render(topicSelectorTemplate, {
					'topics' : topics,
					'reports' : reports,
					'top' : this.top
				})
			);
			
			this.topicSelect = $('#topic_select', this.container)
			.change(function(){
				if(self.topicSelect.val() != '')
					self.topicSelectionChanged.dispatch(self.topicSelect.val());
			});
			
			
			this.reportSelect = $('#report_select', this.container)
			.change(function() {
				if(self.reportSelect.val() != '')
					self.reportSelectionChanged.dispatch(JSON.parse(self.reportSelect.val()));
			});
			if(reportIndex != null) {
				$(this.reportSelect.find('option').get(reportIndex+1)).attr('selected', 'selected');
			}
			this.updateTopicSelection(topicId);
			
			if (navigator.appName == 'Microsoft Internet Explorer')
			{
				function clickHandler(event){
					var el = $(event.data);
					if(event.data != event.target){
						if(el.data("open")) {
							el.css("width", el.data("originalWidth"));
							el.data("open", false);
						}
						else {
							el.data("open", true);
						}
					}
					else{
						setTimeout(function(){
							if(el.data("open")) {
								el.css("width", el.data("originalWidth"));
								el.data("open", false);
							}
							else {
								el.data("open", true);
							}
						}, 100);
					}
					if(typeof clickHandler == 'function') {
						$('body').unbind('mouseup', clickHandler);
					}
					return true;
				}
				var ua = navigator.userAgent;
				var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
				if (re.exec(ua) != null) {
					rv = parseFloat( RegExp.$1 );
					if(rv < 9) {
						this.container.find("select").each(function() {
							var el = $(this);
							el.css("min-width", el.width());
							el.data("originalWidth", el.width());
						}).bind('mousedown',  function(event){
							var el = $(this), elCopy = $(this).clone();
							elCopy.css("width", "auto");
							el.parent().append(elCopy.css('visible', 'false'));
							if(elCopy.width() > el.width()) {
								el.css("width", "auto");
							}
							elCopy.remove();
							el.css("position", 'relative');
							$('body').bind('mouseup', this, clickHandler);
							return true;
						})
						.bind("change blur", function(event){
							var el = $(this);
							el.css("width", el.data("originalWidth"));
							el.data("open", false);
							return true;
						});
						
					}
				}
			}
		},
		
		updateTopicSelection : function(topicId){
			this.topicSelect.val(topicId);
		},
		
		updateReportSelection : function(args) {
			this.reportSelect.val(this.reportSelect.length);
		}
		
	});
});
define('dateCodeUtils', [], {
	
	months : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	
	parseDateCode : function(code, freq){
		// Make sure we are dealing with a string
		code = code.toString();
		var	y = code.substring(0,4),
		date, m;
		
		switch(freq.toUpperCase()){
			case 'A':
				m = 0;
				break;
			case 'Q':
				m = (code.substring(4) - 1) * 3;
				break;
			case 'M':
				m = code.substring(4) - 1;
				break;
		}
		
		if(m !== void 0)
			date = new Date(y, m);
		return date;
	},
	
	createDateCode : function(date, freq){
		var y = date.getFullYear().toString(),
		code, qm;
		
		switch(freq.toUpperCase()){
			case 'A':
				qm = '';
				break;
			case 'Q':
				qm = Math.ceil((date.getMonth() + 1) / 3)
				qm = qm < 10 ? '0' + qm : qm;
				break;
			case 'M':
				qm = date.getMonth() + 1;
				qm = qm < 10 ? '0' + qm : qm;
				break;
		}
		
		if(qm !== void 0)
			code = y + '' + qm;
		return code;
	},
	
	formatDateCode : function(code, freq){
		var y = code.toString().substring(0,4),
		qm = code.toString().substring(4,6),
		ret = '';
		
		switch(freq){
			case 'A':
				ret = y;
				break;
			case 'Q':
				ret = 'Q' + parseInt(qm, 10) + ' ' + y;
				break;
			case 'M':
				ret = this.months[parseInt(qm, 10) - 1] + ' ' + y;
				break;
		}
		
		return ret;
	},
	
	deincrementDateCode : function(code, freq){
		return this.deincrementOrIncrementDateCode(code, freq, false);
	},
	
	incrementDateCode : function(code, freq){
		return this.deincrementOrIncrementDateCode(code, freq, true);
	},
	
	deincrementOrIncrementDateCode : function(code, freq, inc){
		var d = this.parseDateCode(code, freq);
		switch(freq){
			case 'A':
				d = new Date(d.getFullYear() + (inc ? 1 : -1), 0);
				break;
			case 'Q':
				d = new Date(d.getFullYear(), d.getMonth() + (inc ? 3 : -3));
				break;
			case 'M':
				d = new Date(d.getFullYear(), d.getMonth() + (inc ? 1 : -1));
				break;
		}
		
		return this.createDateCode(d, freq);
	}
});

null;

define('text-time_slider.html',[],function () { return '<div id="timeSliderContainer">\r\n\t<div class="labelContainer"></div>\r\n\t<div class="timeSlider{{#selectDateRange}} range{{/selectDateRange}}"></div>\r\n</div>';});

/**
 *	The TimeSlider module create a JQuery UI slider and updates the labels for the slider.
 *  The slider can have one or two thumbs.
 *	
 *	@module timeSlider
 *	@requires  signals, classy, mustache, dateCodeUtils
 */

define('timeSlider', [
		'signals',
		'classy',
		'text-time_slider.html',
		'dateCodeUtils',
		'mustache'
	],
	function(signals, Class, timeSliderTemplate, dateCodeUtils) {

		/**
		 *	Class for the {{#crossLinkModule "timeSlider"}}timeSlider{{/crossLinkModule}} Module
		 * 	@class TimeSlider
		 *	@extends Class
		 *	@constructor 
		 */
		return Class.$extend({
			/**
			 * A string value corresponding to one the classvars for the sliderType.
			 * @property sliderType
			 * @type {String}
			 **/
			sliderType: null,

			/**
			 * A Signal that dispatches when the time selection has changed.
			 * @property timeSelectionChanged
			 * @type {Signal}
			 **/
			timeSelectionChanged: null,
			/**
			 * A Boolean indicating whether to suppress dispatching signals.
			 * @property suppressSignals
			 * @type {Boolean}
			 **/
			suppressSignals: false,
			/**
			 * A Boolean indicating whether the time slider has been created.
			 * @property isCreated
			 * @type {Boolean}
			 **/
			isCreated: false,

			timeSlider: null,

			lowLabel: null,
			highLabel: null,
			lowHandle: null,
			highHandle: null,

			values: null,
			highVal: null,
			lowVal: null,
			freq: null,

			/**
			 *	Constructor Function for timeSlider
			 *	@method __init__
			 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the timeSlider component.
			 */
			__init__: function(container) {
				this.container = container;
				this.timeSelectionChanged = new signals.Signal();
			},

			/**
			 *	This method creates the timeSlider and sets it to have a single or double thumb, and updates the initial values
			 *	@method create
			 *	@param {Object} config An object that must have one key "selectDateRange" that is either a 1 or 0. 1 corresponds to a two thumb slider, 0 corresponds to a single thumb slider.
			 *	@param {String} freq A string representing the frequency. Typically (A|M|Q), but depends on the implementation of the DateCodeUtils.formatDateCode function.
			 *	@param {Array} values An array of values that are the possible values for each tick on the slider.
			 *	@param {Number} lowVal A number. The current low value for the slider.
			 *	@param {Number} highVal A number. The current high value for the slider. Same as lowVal for the single thumb slider.
			 */
			create: function(config, freq, values, lowVal, highVal) {
				var self = this;

				this.freq = freq.toUpperCase();
				this.values = values;
				this.lowVal = lowVal;
				this.highVal = highVal;

				this.container.html(Mustache.render(timeSliderTemplate, config));

				this.timeSlider = $('.timeSlider', this.container);

				this.sliderType = this.timeSlider.hasClass('range') ? this.$class.RANGE_SLIDER : this.$class.SINGLE_SLIDER;

				this.createSlider();

				this.isCreated = true;

				this.updateTimeSelection(freq, values, lowVal, highVal);
			},

			/**
			 *	This method creates the timeSlider.
			 *	@method createSlider
			 */
			createSlider: function() {
				var self = this,
					slide, change, range;

				switch (this.sliderType) {
					case this.$class.RANGE_SLIDER:
						slide = function(event, ui) {
							var step;

							if (ui.values[0] >= ui.values[1] ||
								ui.values[0] < 0 || ui.values[0] > self.values.length ||
								ui.values[1] < 0 || ui.values[1] > self.values.length) {
								return false;
							}

							self.lowVal = self.values[ui.values[0]];
							self.highVal = self.values[ui.values[1]];

							self.updateLabel(true);
							self.updateLabel(false);

							return true;
						};
						change = function(event, ui) {

							self.lowVal = ((ui.values[0] < 0 || ui.values[0] > self.values.length) ? self.values[0] : self.values[ui.values[0]]);
							self.highVal = ((ui.values[1] < 0 || ui.values[1] > self.values.length) ? self.values[self.values.length - 1] : self.values[ui.values[1]])

							self.updateLabel(true);
							self.updateLabel(false);

							if (!self.suppressSignals) self.timeSelectionChanged.dispatch(
								self.lowVal,
								self.highVal
							)
						};
						range = true;
						break;
					case this.$class.SINGLE_SLIDER:
						slide = function(event, ui) {

							self.lowVal = self.values[ui.value];

							self.updateLabel(true);

							return true;
						};
						change = function(event, ui) {

							self.lowVal = self.values[ui.value];

							self.updateLabel(true);

							if (!self.suppressSignals) self.timeSelectionChanged.dispatch(
								self.lowVal
							)
						};
						range = false;
						break;
				}

				this.timeSlider.slider({
					'orientation': 'horizontal',
					'slide': slide,
					'change': change,
					'range': range
				});

				var handles = this.timeSlider.children('.ui-slider-handle');

				this.lowHandle = handles.eq(0);
				this.highHandle = handles.eq(1);

				this.lowLabel = $('<div class="time_slider_tooltip"></div>')
					.append('<span/>')
					.css({
						position: 'absolute'
					})
				if (this.sliderType === this.$class.SINGLE_SLIDER)
					this.lowLabel.appendTo(this.lowHandle);
				else {
					this.highLabel = $('<div class="time_slider_tooltip"></div>')
						.append('<span/>');
					this.lowLabel.appendTo(this.timeSlider);
					this.highLabel.appendTo(this.timeSlider);
					this.lowLabel.css({
						left: 0
					});
					this.highLabel.css({
						position: 'absolute'
					});
				}

				this.updateLabel(true);
				this.updateLabel(false);

				switch (this.sliderType) {
					case this.$class.RANGE_SLIDER:
						this.lowHandle.add(this.highHandle)
							.addClass('dual_slider_handle');
						break;
					case this.$class.SINGLE_SLIDER:
						this.lowHandle.addClass('single_slider_handle');
						break;
				}
			},

			/**
			 *  This method updates the time selection
			 *  @method updateTimeSelection
			 *	@param {String} freq A string representing the frequency. Typically (A|M|Q), but depends on the implementation of the DateCodeUtils.formatDateCode function.
			 *	@param {Array} values An array of values that are the possible values for each tick on the slider.
			 *	@param {Number} lowVal A number. The current low value for the slider.
			 *	@param {Number} highVal A number. The current high value for the slider. Same as lowVal for the single thumb slider.
			 */
			updateTimeSelection: function(freq, values, lowVal, highVal) {
				this.suppressSignals = true;

				this.values = values;
				this.freq = freq;
				this.lowVal = lowVal;
				this.highVal = highVal;

				this.updateTimeSlider();

				this.suppressSignals = false;
			},

			/**
			 *  This method updates the time slider label.
			 *  @method updateLabel
			 *	@param {Boolean} low A Boolean. If true the low value label is updated, if false the high value label is updated.
			 */
			updateLabel: function(low) {
				var label = low ? this.lowLabel : this.highLabel;
				if (label && label.length > 0) {
					var value = low ? this.lowVal : this.highVal,
						handle = low ? this.lowHandle : this.highHandle,
						span = label.find('span');

					span.text(this.formatLabel(value));

					if (this.sliderType === this.$class.SINGLE_SLIDER)
						label.css({
							left: -0.5 * span.width() + 0.5 * handle.width()
						});
					else if (!low)
						label.css({
							left: this.timeSlider.width() - span.width()
						});
				}
			},

			/**
			 *  This method updates the time slider based on the lowVal and highVal properties
			 *  @method updateTimeSlider
			 */
			updateTimeSlider: function() {

				this.timeSlider.slider('option', {
					'min': 0,
					'max': this.values.length - 1
				});

				switch (this.sliderType) {
					case this.$class.RANGE_SLIDER:
						this.timeSlider.slider('option', {
							'values': [
								$.inArray(this.lowVal, this.values),
								$.inArray(this.highVal, this.values)
							]
						});
						break;
					case this.$class.SINGLE_SLIDER:
						this.timeSlider.slider('option', {
							'value': $.inArray(this.lowVal, this.values)
						});
						break;
				}

			},

			formatLabel: function(value) {
				return dateCodeUtils.formatDateCode(value, this.freq);
			},

			__classvars__: {
				/**
				 *  String value to be used as the sliderType.
				 *  @property RANGE_SLIDER
				 *  @type String
				 */
				RANGE_SLIDER: 'rangeSlider',
				/**
				 *  String value to be used as the sliderType.
				 *  @property SINGLE_SLIDER
				 *  @type String
				 */
				SINGLE_SLIDER: 'singleSlider'
			}
		});
	})
;


define('text!templates/time_selector.html',[],function () { return '<div id="timeSliderContainer">\r\n\t<div class="labelContainer"></div>\r\n\t<div class="timeSlider{{#selectDateRange}} range{{/selectDateRange}}"></div>\r\n</div>\r\n<div id="frequencySelectorContainer">\r\n\t<div class="labelContainer"></div>\r\n\t{{#hasAnnual}}\r\n\t<input type="radio" name="freqSelectorGroup" value="A" id="annual"/><label for="annual">Annual</label>\r\n\t{{/hasAnnual}}\r\n\t{{#hasQuarterly}}\r\n\t<input type="radio" name="freqSelectorGroup" value="Q" id="quarterly"/><label for="quarterly">Quarterly</label>\r\n\t{{/hasQuarterly}}\r\n\t{{#hasMonthly}}\r\n\t<input type="radio" name="freqSelectorGroup" value="M" id="monthly"/><label for="monthly">Monthly</label>\r\n\t{{/hasMonthly}}\r\n</div>';});

define('js/view/VC_TimeSelection',[
	'timeSlider',
	'signals',
	'text!templates/time_selector.html'
],
function(TimeSlider, signals, timeSelectorTemplate)
{
	return TimeSlider.$extend({
		
		frequencySelectionChanged : null,
		
		freqSelectors : null,
		
		__init__ : function(container){
			this.$super(container);
			
			this.frequencySelectionChanged = new signals.Signal();
		},
		
		create : function(config, freq, values, lowVal, highVal){
			var self = this;
			
			this.freq = freq;
			this.values = values;
			this.lowVal = lowVal;
			this.highVal = highVal;
			
			this.container.html(Mustache.render(timeSelectorTemplate, config));
			
			$('#frequencySelectorContainer', this.container).buttonset();
			this.freqSelectors = $('input:radio[name=freqSelectorGroup]', this.container);
				
			this.freqSelectors.change(function(){
				if(!self.suppressSignals) self.frequencySelectionChanged.dispatch(
					self.freqSelectors.filter(':checked').val()
				);
			});
			
			this.timeSlider = $('.timeSlider', this.container);
			
			this.sliderType = this.timeSlider.hasClass('range') ? this.$class.RANGE_SLIDER : this.$class.SINGLE_SLIDER;
			
			this.createSlider();
			
			this.isCreated = true;
			
			this.updateTimeSelection(freq, values, lowVal, highVal);
		},
		
		updateTimeSelection : function(freq, values, lowVal, highVal){
			this.$super(freq, values, lowVal, highVal);
			
			this.suppressSignals = true;

			this.updateFreqSelectors();
			
			this.suppressSignals = false;
		},
		
		updateFreqSelectors : function(){
			this.freqSelectors.filter('[value=' + this.freq + ']').attr('checked', true);
			$('#frequencySelectorContainer', this.container).buttonset('refresh');
		}
	});
});

define('text!templates/analysisBox.html',[],function () { return '<div class="style2">\r\n\t<h3>Related Analysis</h3>\r\n\t<ul class="analysis_menu">\r\n\t\t{{#relatedAnalysis}}\r\n\t\t<li>{{TITLE}}</li>\r\n\t\t{{/relatedAnalysis}}\r\n\t</ul>\r\n\t{{#moreAnalysisURL}}More...{{/moreAnalysisURL}}\r\n\t<br/>\r\n\t<h3>Electricity Monthly Update</h3>\r\n\tElectricity Monthly Update\r\n\t<h3>Additional Insight & Data</h3>\r\n\t<div class="add_insight">\r\n\t\t<form id="historical" name="historical">\r\n\t\t\t<label for="h_data">Historical Reports & Data Files<br/>\r\n\t\t\t<select class="link_prompt" id="h_data" style="width:204px" size="1" name="h_data" onchange="window.location.href = this.value;">\r\n\t\t\t\t<option class="link_prompt" value="">Jump to</option>\r\n\t\t\t\t<option class="link_option" value="/aer">Annual Energy Review</option>\r\n\t\t\t\t<option class="link_option" value="/totalenergy/data/monthly/">Monthly Energy Review</option>\r\n\t\t\t\t<option class="link_option" value="/electricity/annual/">Electric Power Annual</option>\r\n\t\t\t\t<option class="link_option" value="/cneaf/electricity/page/data.html">Detailed Databases</option>\r\n\t\t\t</select>\r\n\t\t\t</label>\r\n\t\t</form>\r\n\r\n\t\t<form id="projections" name="projections">\r\n\t\t\t<label for="p_data">Projections: <br/>\r\n\t\t\t<select class="link_prompt" id="p_data" style="width:204px" size="1" name="p_data" onchange="window.location.href = this.value;">\r\n\t\t\t\t<option class="link_prompt" value="">Jump to</option>\r\n\t\t\t\t<option class="link_option" value="/forecasts/aeo/">Annual Energy Outlook</option>\r\n\t\t\t\t<option class="link_option" value="/forecasts/steo/">Short-Term Energy Outlook</option>\r\n\t\t\t</select>\r\n\t\t\t</label>\r\n\t\t</form>\r\n\t</div>\r\n</div>';});

define('js/view/VC_Analysis',[
	'classy',
	'text!templates/analysisBox.html',
	'signals',
	'mustache'
],function(
	Class, 
	analysisBoxTemplate, 
	signals
) {
	return 	Class.$extend({
		__init__ : function(container){
			var self = this;
			
			this.container = container;
			this.button = $('#main_analysis_button');
			this.analysisButtonClicked = new signals.Signal();
			
			this.button.click( function(e){
				if(!self.button.hasClass('disabled'))
					self.analysisButtonClicked.dispatch();
			});
		},
		
		generateAnalysis : function(analysisContainer, relatedAnalysis, moreAnalysisURL){
			
			analysisContainer.empty();
			
			// Generate filterMenu object within mustache
			var analysisMenu = $(Mustache.to_html(analysisBoxTemplate,{
				relatedAnalysis : relatedAnalysis.slice(0, 4),
				moreAnalysisURL: relatedAnalysis.length > 5 ? moreAnalysisURL : void 0
			}));
			
			analysisContainer.append(analysisMenu);
		},

		showAnalysis : function(relatedAnalysis, moreAnalysisURL){
			
			var self = this;
			var buttonOffset = self.button.offset();
			
			var modalCloseHandler = function(e){
				if($(e.target).parents('#analysis_dialog_container').length == 0){
					$.modal.close();
					if(typeof modalCloseHandler == 'function') {
						$('body').unbind('click', modalCloseHandler);
					}
				}
			};

			this.generateAnalysis(
				this.container,
				relatedAnalysis,
				moreAnalysisURL
			);

			$('#analysis_dialog_container').modal({
				'autoPosition' : false,
				'overlayClose' : true,
				'fixed' : false,
				'close' : false,
				'persist' : true
			});
			
			// Assuming this is called in response to a mouse click on a
			// element other than the body, then the current click event
			// will bubble up to the body, so we attach the actual click
			// handler then.
			$('body').one('click', function(){
				$('body').bind('click', modalCloseHandler);
			});
			
			$('#simplemodal-container').css({
				'top' : buttonOffset.top,
				'left' : buttonOffset.left
			});
			
			this.container.css({
				left : (this.button.outerWidth() - this.container.outerWidth()) / 2
			});
			
			$(window).resize(this.resizeCallback = function(){
				var buttonOffset = self.button.offset();
				$('#simplemodal-container').css({
					'top' : buttonOffset.top,
					'left' : buttonOffset.left
				})
			});
		},
		
		hideDownload : function(){
			$.modal.close();
			if(typeof this.resizeCallback == 'function') {
				$(window).unbind('resize', this.resizeCallback);
			}
			this.resizeCallback = null;
		}
	});
});

define('text!templates/lineChart.html',[],function () { return '<div id="lineChartContainer"></div>\r\n<div class="chartIndexing">\r\n\t<form id="chartOptions">\r\n\t\t<div id="indexContainer">\r\n\t\t\t<span class="optionsLabel">Chart indexing options: </span>\r\n\t\t\t<input type="radio" id="index0" name="index" value="0" checked="checked"/><label for="index0">None</label>\r\n\t\t\t<input type="radio" id="index1" name="index" value="1"/><label for="index1">Index to start as percent</label> \r\n\t\t\t<input type="radio" id="index2" name="index" value="2"/><label for="index2">Index to start as value</label>\r\n\t\t</div>\r\n\t</form>\r\n</div>';});

define('js/view/VC_LineChart',[
	'signals',
	'classy',
	'js/view/DateCodeUtil',
	'text!templates/lineChart.html'
],
function(signals, Class, DateCodeUtil, chartTemplate){
	
	return Class.$extend({
		
		container : null,
		
		chart : null,
		
		indexingChanged : null,
		
		indexing : null,
		
		data : null,
		
		dataMap : null,
		
		dataColumns : null,
		
		freq : null,
		
		units : null,
		
		axis : null,
		
		series : null,
		
		seriesMap : null,
		
		seriesColorQueue : null,
		
		__init__ : function(container){
            String.prototype.replaceAll = function(search, replace)
            {
                //if replace is null, return original string otherwise it will
                //replace search string with 'undefined'.
                if(!replace)
                    return this;

                return this.replace(new RegExp('[' + search + ']', 'g'), replace);
            };

            var self = this;
			
			this.indexing = null;
			this.data = [];
			this.dataMap = {},
			this.dataColumns = [],
			this.freq = '';
			this.units = {},
			this.axis = [],
			this.series = [],
			this.seriesMap = {}
			this.seriesColorQueue = this.$class.seriesColors.slice(0);
			
			this.container = container;
			this.indexingChanged = new signals.Signal();
			
			this.container.html(Mustache.render(chartTemplate));
			
			$('#indexContainer', this.container).buttonset();
			
			$('form input[name="index"]', this.container).on('change', function(event) {
				self.indexingChanged.dispatch($(event.target).val());
			});
		},
		
		createSeriesOpts : function(datum, dataColumns, freq, showUnits, usePinnedName){
			var seriesOpts = {
				'data' : [],
				'color' : this.claimNextColor(),
				'tooltip' : {
					'units' : datum['UNITS']
				},
				'xType' : function(){
					switch(freq) {
						case 'A':
							return Highcharts.xTypes.annual;
						case 'Q':
							return Highcharts.xTypes.quarterly;
						case 'M':
							return Highcharts.xTypes.monthly;
					}
				}()
			},
			seriesName = seriesOpts['name'] = (usePinnedName ? datum['PINNED_NAME'] :  datum['CHART_NAME']) + (showUnits ? ' (' + datum['UNITS'] + ')' : ''),
			cur,
			tmp, j;
			
			for(j=0; j<dataColumns.length; j++){
				var columnKey = parseInt(dataColumns[j].toString());
				seriesOpts['data'].push([
					DateCodeUtil.parseDateCode(columnKey, freq).getTime(),
                    !isNaN(tmp = parseFloat(datum['DATA'][columnKey])) ? tmp : (datum['DATA'][columnKey] != 'W' ? 0 : null)

                ]);
			}
			
			return seriesOpts;
		},
		
		resetColorQueue : function(){
			this.seriesColorQueue = this.$class.seriesColors.slice(0);
		},
		
		claimNextColor : function(){
			color = this.seriesColorQueue.shift();
			this.seriesColorQueue.push(color);
			return color;
		},
		
		releaseColor : function(color){
			this.seriesColorQueue.splice(
				$.inArray(
					color,
					this.seriesColorQueue
				),
				1
			);
			this.seriesColorQueue.unshift(color);
		},
		
		update : function(title, data, dataColumns, freq, indexing, start, end, usePinnedName, userGenerated){
			var self = this,
			seriesOpts = [],
			axis = [], units = {},
			i, chartOpts, datum, unit;
			
			this.data = data;
			this.dataColumns = dataColumns;
			this.freq = freq;
			this.indexing = indexing;
			this.seriesMap = {};
			
			this.resetColorQueue();
			this.setIndexed(this.indexing);
			
			if(this.indexing != 1){
				for(i=0; i<this.data.length; i++){
					if(data[i] != null ) {
						datum = data[i];
						unit = datum['UNITS'];
						
						if(!(unit in units)){
							units[unit] = axis.length;
							axis.push($.extend(true, {
								'title' : {
									'text' : unit
								},
								'opposite' : axis.length % 2
							}, this.$class.defaultYAxisOptions))
						}
					}
				}
			}
			else{
				unit = 'Percent';
				units[unit] = 0;
				axis.push($.extend({
					'title' : {
						'text' : unit
					}
				}, this.$class.defaultYAxisOptions))
			}
			
			for(i=0; i<this.data.length; i++){
				if(data[i] != null ) {
					datum = data[i];
					var curr_series = null;
					if(((indexing === '1' || indexing === '2') && !isNaN(datum.DATA[start])) || (indexing === '0' || typeof indexing == 'undefined')) {
						curr_series = $.extend(
							this.createSeriesOpts(datum, dataColumns, freq, axis.length > 1 ? true : false, usePinnedName),{
								'yAxis' : indexing === '1' ? 0 : units[datum['UNITS']]
							}
						);
						seriesOpts.push(curr_series);
					}
				}
				else {
					this.claimNextColor();
				}
			}
			
			this.destroy();
			
			chartOpts = $.extend(true, {
				'chart' : {
					'renderTo' : $('#lineChartContainer', this.container).get(0),
					'spacingBottom' : 0
				},
				'title' : {
					'text' : title
				},
				'yAxis' : axis,
				'xAxis' : {
					'min' : DateCodeUtil.parseDateCode(start, freq).getTime(),
					'max' : DateCodeUtil.parseDateCode(end, freq).getTime()
				},
				'series' : seriesOpts,
				'userGenerated' : userGenerated,
                exporting : {
                    filename: title.replaceAll(':',',').replaceAll(' ','_')
                }
			}, this.$class.defaultChartOptions);


            // embed options
            $.extend(true, chartOpts, {
                'exporting' : {
                    'embedOptions' : {
                        'series_id' : null,
                        'analysis' : this.indexing == 2 ? 'indexval' : (this.indexing == 1 ? 'indexpct' : 'none'),
                        'relation_mode' : 'line'
                    }
                }
            });
            // get series_ids for embedOptions
            var series_id = [];
            for(var i = 0; i < data.length; i++) {
                if(data[i] != null) {
                    series_id.push(data[i].SERIES_ID);
                }
            }
            if(series_id.length > 0) { chartOpts.exporting.embedOptions.series_id = series_id.join(';');}


            /*var points = DateCodeUtil.getNumberOfDataPoints(start, end, freq);
            switch(freq) {
                case 'A':
                    chartOpts['xAxis']['tickInterval'] = (365*24*3600*1000)*Math.ceil(points/10);
                    break;
                case 'Q':
                    chartOpts['xAxis']['tickInterval'] = (365*24*3600*1000/4)*Math.ceil(points/10);
                    break;
                case 'M':
                    chartOpts['xAxis']['tickInterval'] = (365*24*3600*1000/12)*Math.ceil(points/10);
                    break;
            }*/
			if(freq == 'A') {
				chartOpts['xAxis']['tickInterval'] = 365*24*3600*1000;
			}
			switch(indexing){
				case '1':
					$.extend(true, chartOpts, {
						'plotOptions': {
							'series':{
								'compare': 'percent',
								'tooltip' : {
									'precision' : 2
								}
							}
						},
						'subtitle' : {
							'text' : 'Indexed to ' + DateCodeUtil.formatDateCode(start, freq) + ' as percent'
						},
						'yAxis' : Highcharts.map(chartOpts.yAxis, function(axisOpts){
							return $.extend(true, axisOpts, {
								'min' : null,
								'title': {
									'align' : chartOpts.yAxis.length > 2 ? 'middle' : 'above'
								}
							});
						})
					});
					break;
				case '2':
					$.extend(true, chartOpts, {
						'plotOptions': {
							'series':{
								'compare': 'value'
							}
						},
						'subtitle' : {
							'text' : 'Indexed to ' + DateCodeUtil.formatDateCode(start, freq) + ' as value'
						},
						'yAxis' : Highcharts.map(chartOpts.yAxis, function(axisOpts){
							return $.extend(true, axisOpts, {
								'min' : null,
								'title' : {
									'align' : chartOpts.yAxis.length > 2 ? 'middle' : 'above'
								}
							});
						})
					});
					break;
				default:
					$.extend(true, chartOpts, {
						'plotOptions': {
							'series':{
								'compare': null
							}
						},
						'yAxis' : Highcharts.map(chartOpts.yAxis, function(axisOpts){
							return $.extend(true, axisOpts, {
								'min' : 0,
								'title' : {
									'align' : chartOpts.yAxis.length > 2 ? 'middle' : 'above'
								}
							});
						})
					});
					break;
			}
			this.units = units;
			this.axis = axis;
			this.chart = new this.$class.chartConstructor(chartOpts);
			this.series = this.chart.series.slice(0);
			$.each(this.series, function(i, s){
				self.seriesMap[s.name] = i;
			});
		},
		
		setStartEnd : function(start, end, freq){
			if(this.chart.xAxis && this.chart.xAxis.length > 0) {
				this.chart.xAxis[0].setExtremes(
					DateCodeUtil.parseDateCode(start, freq).getTime(),
					DateCodeUtil.parseDateCode(end, freq).getTime()
				);
			}
			switch(this.indexing){
				case '1':
					this.chart.setTitle(null, {
						'text' : 'Indexed to ' + DateCodeUtil.formatDateCode(start, freq) + ' as percent'
					});
					break;
				case '2':
					this.chart.setTitle(null, {
						'text' : 'Indexed to ' + DateCodeUtil.formatDateCode(start, freq) + ' as value'
					});
					break;
				default:
					break;
			}
		},
		
		download : function(type){
			if(this.chart){
				if(type == 'csv')
					this.chart.generateCSV();
				else{
					var options;
                    switch(type){
                        case 'pdf':
                            options = {
                                url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
                                type: 'application/pdf'
                            };
                            break;
                        case 'svg':
                            options = {
                                url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
                                type: 'image/svg+xml'
                            };
                            break;
                        case 'png':
                        default:
                            options = {
                                url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
                                type: 'image/png'
                            };
                            break;
                    }
					
					this.chart.exportChart(options);
				}
			}
		},

        embed: function() {
            this.chart.openAPIEmbed();
        },
		
		setIndexed : function(indexed){
			$('form input[name="index"]')
			.filter('[value=' + indexed + ']')
			.attr('checked', true);
			$('#indexContainer', this.container).buttonset('refresh');
		},
		
		removeSeries : function(datum){
			this.highchartSeries[datum['CHART_NAME']].remove();
			delete this.seriesOptions[datum['CHART_NAME']];
		},
		
		destroy : function() {
			if(this.chart != null) {
				if(this.chart.hasRendered == true) {
					this.chart.destroy();
				}
				$('.chartCaptionsContainer', this.container).remove();
				this.chart = null;
			}
		},
		
		__classvars__ : {
			
			chartConstructor : Highcharts.Chart,
			
			seriesColors : Highcharts.getOptions()['colors'],
			
			defaultYAxisOptions : {
				'labels' : {
					'x' : null,
					'y' : null
				}
			},
			
			defaultChartOptions : {
				'chart' : {
					'zoomType' : '',
					'panning' : false,
					'lineUpYAxisZeros' : true
				},
				'xAxis' : {
					'maxZoom' : 1,
					'type' : 'datetime',
					'labels' : {
						y : 20
					},
					'startOnTick': false,
					'showFirstLabel': true
				},
				'navigator' : {
					'enabled' : false
				},
				'rangeSelector' : {
					'enabled' : false
				},
				'scrollbar' : {
					'enabled' : false
				}
			}
		}
		
	});
});

define('text!templates/columnChart.html',[],function () { return '<div id="columnChartContainer"></div>\r\n<div class="chartIndexing">\r\n\t<form id="chartOptions">\r\n\t\t<div id="indexContainer">\r\n\t\t\t<div id="columnValuesContainer">\r\n\t\t\t\t<span class="optionsLabel">Value options:</span>\r\n\t\t\t\t<input type="radio" id="columnValues0" name="columnvalues" value="0" checked="checked"/><label for="columnValues0">Show values only</label>\r\n\t\t\t\t<input type="radio" id="columnValues1" name="columnvalues" value="1"/><label for="columnValues1">Show percent change only</label>\r\n\t\t\t</div>\r\n\t\t\t<div id="columnEndpointsContainer">\r\n\t\t\t\t<span class="optionsLabel">Series options:</span>\r\n\t\t\t\t<input type="radio" id="columnEndpoints0" name="columnendpoints" value="0" checked="checked"><label for="columnEndpoints0">Graph first and last</label>\r\n\t\t\t\t<input type="radio" id="columnEndpoints1" name="columnendpoints" value="1"><label for="columnEndpoints1">Graph first</label>\r\n\t\t\t\t<input type="radio" id="columnEndpoints2" name="columnendpoints" value="2"><label for="columnEndpoints2">Graph last</label>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</form>\r\n</div>';});

define('js/view/VC_ColumnChart',[
	'signals',
	'classy',
	'js/view/DateCodeUtil',
	'text!templates/columnChart.html',
	'mustache'
], function(signals, Class, DateCodeUtil, chartTemplate){
	return Class.$extend({
	
		seriesColorQueue : null,
		
		__init__ : function(container){
            String.prototype.replaceAll = function(search, replace)
            {
                //if replace is null, return original string otherwise it will
                //replace search string with 'undefined'.
                if(!replace)
                    return this;

                return this.replace(new RegExp('[' + search + ']', 'g'), replace);
            };

			var self = this;
			this.container = container;
			this.container.html(Mustache.render(chartTemplate));
			this.endpointSelectionChanged = new signals.Signal();
			this.valuesSelectionChanged = new signals.Signal();
			this.data = [];
			this.dataMap = {};
			this.seriesColorQueue = this.$class.seriesColors.slice(0);
			
			$('#columnValuesContainer', this.container).buttonset();
			$('#columnEndpointsContainer', this.container).buttonset();
			
			$('form input[name="columnendpoints"]', this.container).on('change', function(event) {
				self.endpointSelectionChanged.dispatch($(event.target).val());
			});
			$('form input[name="columnvalues"]', this.container).on('change', function(event) {
				self.valuesSelectionChanged.dispatch($(event.target).val());
			});
			
		},
		update : function(title, data, dataColumns, freq, start, end, endpoints, values, usePinnedName, userGenerated){
			var self = this;
			var categories = [];
			var series = [];
			var yAxis = [];
			var changePrefix = 'Change ';
			var changePrecision = 2;
			var uniqueUnits = {};
			
			var showValues = values == '0';
			var showChange = values == '1';
			
			// Default
			if(!showValues && !showChange) showValues = true;
			
			if(showChange) {
				yAxis.push({
					title : {
						text : '% Change'
					}
				});
			}
			
			this.resetColorQueue();
			
			$.each(data, function(i, d){
				if(showValues && d != null){
					if(!(d['UNITS'] in uniqueUnits))
						uniqueUnits[d['UNITS']] = yAxis.push({
							title : {
								text : d['UNITS']
							},
							opposite : (yAxis.length) % 2
						}) - 1;
				}
			});
			
			$.each(data, function(i, d){
				if(d != null) {
					series.push({
						name : (usePinnedName ? d['PINNED_NAME'] : d['CHART_NAME']) + (yAxis.length > 1 ? ' (' + d['UNITS'] + ')' : ''),
						yAxis : showValues ? uniqueUnits[d['UNITS']] : 0,
						data : function(){
								var ret = [];
								if(showValues){
									var startVal = d['DATA'][start];
									var endVal = d['DATA'][end];
									switch(endpoints){
										case '1':
											!isNaN(startVal) ? ret.push(startVal) : ret.push(null);
											break;
										case '2':
											!isNaN(endVal) ? ret.push(endVal) : ret.push(null);
											break;
										case '0':
										default:
											!isNaN(startVal) ? ret.push(startVal) : ret.push(null);
											!isNaN(endVal) ? ret.push(endVal) : ret.push(null);
											break;
									}
								}
								else{
									var y0 = d['DATA'][start];
									var y1 = d['DATA'][end];
									if(!isNaN(y0) && !isNaN(y1) && y0 > 0)
										ret.push((y1- y0) / y0 * 100);
									else
										ret.push(null);
								}
								return ret;
							}(),
						tooltip : showChange ? {precision : changePrecision} : void 0,
						color : self.claimNextColor()
					});
				}
				else {
					self.claimNextColor();
				}
			});
			
			var chartOpts = $.extend(true, {
				chart : {
					renderTo : $('#columnChartContainer', this.container).get(0)
				},
				title : {
					text : title
				},
				series : series,
				xAxis : {
					categories : function(){
						var ret = [];
						startVal = DateCodeUtil.formatDateCode(start, freq);
						endVal = DateCodeUtil.formatDateCode(end, freq);
						if(showValues)
							switch(endpoints){
								case '1':
									ret.push(startVal);
									break;
								case '2':
									ret.push(endVal);
									break;
								case '0':
								default:
									ret.push(startVal);
									ret.push(endVal);
									break;
							}
						else
							ret.push(changePrefix + startVal + '-' + endVal)
						return ret;
					}(),
					min : function() {
						switch(values) {
							case "1":
								return 0;
							case "0":
							default:
								switch(endpoints) {
									case "1":
									case "2":
										return 0;
									case 0:
									default:
										return null;
								}
								return null;
						}
						
						return null;
					}(),
					max : function() {
						switch(values) {
							case "1":
								return 0;
							case "0":
							default:
								switch(endpoints) {
									case "1":
									case "2":
										return 0;
									default:
										return null;
								}
						}
						
						return null;
					}()
				},
				yAxis : $.map(yAxis, function(axis){
					$.extend(axis.title, {align : yAxis.length > 2 ? 'middle' : void 0});
					return axis;
				}),
				userGenerated : userGenerated,
                exporting : {
                    filename: title.replaceAll(':',',').replaceAll(' ','_')
                }
			}, this.$class.defaultChartOptions);

            // embed options
            $.extend(true, chartOpts, {
                'exporting' : {
                    'embedOptions' : {
                        'series_id' : null,
                        'analysis' : 'none',
                        'relation_mode' : 'column'
                    }
                }
            });
            // get series_ids for embedOptions
            var series_id = [];
            for(var i = 0; i < data.length; i++) {
                if(data[i])
                    series_id.push(data[i].SERIES_ID);
            }
            if(series_id.length > 0) { chartOpts.exporting.embedOptions.series_id = series_id.join(';');}


            this.usePinnedName = usePinnedName;
			this.userGenerated = userGenerated;
			this.title = title;
			this.data = data;
			this.dataColumns = dataColumns;
			this.freq = freq;
			this.start = start;
			this.end = end;
			this.setEndpoints(endpoints);
			this.setValues(values);
			
			this.destroy();
			this.chart = new Highcharts.Chart(chartOpts);
		},
		
		download : function(type){
			if(this.chart){
				if(type == 'csv')
					this.chart.generateCSV();
				else{
					var options;
					switch(type){
						case 'pdf':
							options = {
								url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
								type: 'application/pdf'
							};
							break;
                        case 'svg':
                            options = {
                                url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
                                type: 'image/svg+xml'
                            };
                            break;
						case 'png':
						default:
							options = {
								url : '/global/scripts/jquery/highcharts/exporting-server/index.php',
                                type: 'image/png'
							};
							break;
					}
					
					this.chart.exportChart(options);
				}
			}
		},

        embed: function() {
            this.chart.openAPIEmbed();
        },
		
		setEndpoints : function(endpoints){
			$('form input[name="columnendpoints"]', this.container)
				.filter('[value=' + endpoints + ']')
				.attr('checked', true);
			$('#columnEndpointsContainer', this.container).buttonset('refresh');
			this.endpoints = endpoints;
			
		},
		
		setValues : function(values){
			$('form input[name="columnvalues"]', this.container)
				.filter('[value=' + values + ']')
				.attr('checked', true);
			$('#columnValuesContainer', this.container).buttonset('refresh');
			
			this.values = values;
		},
		
		setStartEnd : function(start, end, freq){
			this.update(this.title, this.data, this.dataColumns, freq, start, end, this.endpoints, this.values, this.usePinnedName, this.userGenerated);
		},
		
		resetColorQueue : function(){
			this.seriesColorQueue = this.$class.seriesColors.slice(0);
		},
		
		claimNextColor : function(){
			color = this.seriesColorQueue.shift();
			this.seriesColorQueue.push(color);
			return color;
		},
		
		releaseColor : function(color){
			this.seriesColorQueue.splice(
				$.inArray(
					color,
					this.seriesColorQueue
				),
				1
			);
			this.seriesColorQueue.unshift(color);
		},
		
		destroy : function(){
			if(this.chart != null) {
				if(this.chart.hasRendered == true) {
					this.chart.destroy();
				}
				$('.chartCaptionsContainer', this.container).remove();
				this.chart = null;
			}
		},
		
		__classvars__ : {
			
			seriesColors : Highcharts.getOptions()['colors'],
			
			defaultChartOptions : {
				chart : {
					defaultSeriesType : 'column',
					lineUpYAxisZeros : true,
					spacingBottom : 0
				}
			}
		}
	})
});
/**
 *	The 'animatedComponent' module has play/stop functions to play through an animation.
 *
 *	@module animatedComponent
 */

 define('animatedComponent', ['classy'],
function(Class){
	
	/**
	 *	Class for the {{#crossLinkModule "animatedComponent"}}animatedComponent{{/crossLinkModule}} Module
	 * 	@class AnimatedComponent
	 *	@extends Class
	 *	@constructor 
	 */
	return Class.$extend({
		/**
		 *	Constructor Function for animatedComponent
		 *	@method __init__
		 */
		__init__ : function(){
			this.animation = {
				enabled : false,
				stepSize : 1000,
				iteration : 0,
				interval : null
			};
		},
		
		update : function(animation){
			$.extend(
				this.animation,
				animation
			);
		},
		
		play : function(loop, stepSize){
			var self = this;
			
			var animate = function() {
				var iteration;
				if(self.animation.maxIteration){
					if(loop)
						iteration = self.animation.iteration % self.animation.maxIteration;
					else{
						iteration = Math.min(self.animation.iteration, self.animation.maxIteration);
						if(iteration == self.animation.maxIteration)
							self.stop();
					}
				}
				if (self.animation.callback) self.animation.callback.call(self, iteration);
				if(self.animation.maxIteration === void 0 || iteration + 1 <= self.animation.maxIteration)
					self.animation.iteration = iteration + 1;
			};
			
			if(this.animation.interval) this.stop();
			animate();
			this.animation.interval = setInterval(animate, stepSize || this.animation.stepSize);
		},
		
		stop : function(){
			clearInterval(this.animation.interval);
			this.animation.interval = null;
		},
		
		reset : function(iteration){
			this.animation.iteration = Math.min(iteration, this.animation.maxIteration);
			if(this.animation.callback) this.animation.callback.call(this, iteration);
		},
		
		destroy : function() {
			this.stop();
		}
	});
});



null;


/**
 *	The 'map' module does stuff.
 *
 *	@module map
 *	@requires animatedComponent, signals, jVectorMap, usa-en
 */
 
define('map',
[
	'animatedComponent',
	'signals',
	'order!jVectorMap',
	'order!usa-en'
],
function(AnimatedComponent, signals, mapTemplate){
	
	/**
	 *	Class for the {{#crossLinkModule "map"}}map{{/crossLinkModule}} Module
	 * 	@class Map
	 *	@extends AnimatedComponent
	 *	@constructor 
	 */
	return AnimatedComponent.$extend({
		/**
		 *	Constructor Function for map
		 *	@method __init__
		 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the map component.
		 */
		__init__ : function(container, basemap){
			var self = this;
			
			this.$super();
			
			this.container = container;
			this.regionClicked =  new signals.Signal();
			this.markerClicked =  new signals.Signal();
			
			this.mapContainer = $('#mapContainer', this.container);
			this.legendContainer = $('#map_legend', this.container);
			this.titleContainer = $('#mapTitle', this.container);
			
			if(this.titleContainer.length == 0)
				this.titleContainer = $('<h2 id="mapTitle"></h2>').appendTo(this.container);
			if(this.mapContainer.length == 0)
				this.mapContainer = $('<div id="mapContainer"></div>').appendTo(this.container);
			if(this.legendContainer.length == 0)
				this.legendContainer = $('<div id="map_legend"></div>').appendTo(this.container);
			
			this.counts = null;
			
			this.map = null;
			
			this.colors = {};
			
			this.basemap = (basemap == null ? 'usa_en' : basemap);
			
			this.options = {
				animation : {
					callback : function(iteration){
						if(this.colors) {
							this.worldMap.series.regions[0].setValues(this.getCurrentColors());
						}
						else if(this.worldMap.series.regions && this.worldMap.series.regions.length > 0) {
							this.worldMap.series.regions[0].setValues(this.getCurrentCounts());
						}
						else if(this.worldMap.series.markers && this.worldMap.series.markers.length > 0) {
							this.worldMap.series.markers[0].setValues(this.getCurrentCounts());
						}
					}
				},
				map: this.basemap,
				normalizeFunction: 'polynomial',
				hoverOpacity: 0.7,
				hoverColor: false,
				backgroundColor: 'white',
				dataPrecision : 0
			};
		},
		
		play : function(loop, stepSize){
			this.$super(loop, stepSize);
		},
		
		update : function(counts, title, options){
			var self = this;
			
			if(typeof options != 'undefined' && typeof options.map != 'undefined') {
				this.basemap = options.map;
				this.options.map = options.map;
			}
			
			this.counts = $.map(counts instanceof Array ? counts : [counts], function(count){
				var newCount = {};
				for(var key in count){
					if(count.hasOwnProperty(key)){
						if(jvm.WorldMap.maps[self.basemap].paths.hasOwnProperty(key.toUpperCase())) {
							newCount[key.toUpperCase()] = count[key];
						}
						else if(jvm.WorldMap.maps[self.basemap].paths.hasOwnProperty(key.toLowerCase())) {
							newCount[key.toLowerCase()] = count[key];
						}
						else {
							newCount[key] = count[key];
						}
					}
				}
				return newCount;
			});
			
			this.options = $.extend(this.options || {}, options);
			
			this.$super($.extend(
				true,
				this.animation,
				this.options.animation,
				{
					maxIteration : counts.length - 1
				})
			);
			
			this.setTitle(title);
			
			this.calculateMinMax();
			if(typeof this.assignColors == 'function')
				this.assignColors();
				
			this.createLegend();
			
			this.setContainerHeight();
			
			this.createMap();
		},
		
		setContainerHeight : function() {
			this.mapContainer.height(
				this.mapContainer.parent().height() - 
					(this.titleContainer.outerHeight(true) + 
					this.legendContainer.outerHeight(true) + 
					parseInt(this.mapContainer.css('paddingTop')) +  
					parseInt(this.mapContainer.css('paddingBottom')) + 
					10)
				);
		},
		
		calculateMinMax : function() {
			this.min = this.max = null;
			for (var i = 0; i < this.counts.length; i++) {
				for (var state in this.counts[i]) {
					if(this.counts[i].hasOwnProperty(state)){
						this.min = ((this.min == null || this.counts[i][state] < this.min) ? this.counts[i][state] : this.min);
						this.max = ((this.max == null || this.counts[i][state] > this.max) ? this.counts[i][state] : this.max);
					}
				}
			}

			if(this.min !== null)
				this.min = parseFloat(Highcharts.numberFormat(this.min, this.options.dataPrecision, '.', ''));
			if(this.max !== null)
				this.max = parseFloat(Highcharts.numberFormat(this.max, this.options.dataPrecision, '.', ''));
		},
		
		setTitle : function(title){
			this.titleContainer.text(title);
		},

		createRegionOps : function(){
			return [{
				values : this.colors ? this.getCurrentColors() : this.getCurrentCounts(),
				min : this.min,
				max : this.max,
				scale : this.colors ? void 0 : this.$class.palette
			}];
		},

		createMarkerOps : function(){//cjd
			return this.options.markers ? [{
				values : this.colors ? this.getCurrentColors() : this.getCurrentCounts(),
				min : this.min,
				max : this.max,
				scale : this.colors ? void 0 : this.$class.palette
			}] : null;
		},

		createMap : function(){
			var self = this;
			
			if(this.worldMap){
				this.worldMap.remove();
			}
			this.regionOps = this.createRegionOps();
			this.markerOps = this.createMarkerOps();
			var series = {};
			if(this.regionOps) {
				series['regions'] = this.regionOps;
			}
			if(this.markerOps) {
				series['markers'] = this.markerOps;
			}
			this.mapContainer.empty();
			this.map = $('<div style="height:100%;width:100%;" class="map_inside_container"></div>').appendTo(this.mapContainer)
			.vectorMap($.extend(true, {
				regionStyle: {
					initial: {
						fill: '#CCCCCC',
						"fill-opacity": 1,
						stroke: 'none',
						"stroke-width": 0,
						"stroke-opacity": 1
					},
					hover: {
						"fill-opacity": 0.8
					},
					selected: {
						fill: 'yellow'
					}
				},
				series : series,
				onRegionClick : function(event, code) {
					self.regionClicked.dispatch(code.toUpperCase(), event);
				},
				onMarkerClick : function(event, code) {
					self.markerClicked.dispatch(self.options.markers[code], event);
				},
				zoomOnScroll : false
			}, this.options));

			this.worldMap = this.mapContainer.find('.jvectormap-container').data('mapObject');
		},
		
		createLegend : function(buckets, colors){
			// Implemented by subclasses
		},
		
		getCurrentColors : function(){
			var regions = {};
			$.each(jvm.WorldMap.maps[this.basemap].paths, function(key, value) {
				regions[key] = "";
			});
			return $.extend(regions, this.colors[this.animation.iteration]);
		},
		
		getCurrentCounts : function(){
			return this.counts[this.animation.iteration];
		},
		
		destroy : function() {
			if(this.worldMap) {
				this.worldMap.remove();
			}
			this.mapContainer.empty();
			this.legendContainer.empty();
			this.titleContainer.empty();
		},
		
		assignColors : function(counts, buckets, palette){

		},

		exportMap: function(fileType) {
			if(!window.SVGAngle) {
				throw("SVG not supported");
			}
			
			var mapBackground="white";
			var svg = $('.jvectormap-container').html();
			var newSvg = $('.jvectormap-container').html();
			var mapHeight = $('.jvectormap-container').find('svg').attr('height');
			mapHeight=parseInt(mapHeight)+20;
						
			var heightI = svg.indexOf("height");
			heightI = svg.indexOf('"',heightI);
			var heightIe = svg.indexOf('"',heightI+1);
			svg = svg.slice(0, heightI+1) + mapHeight + svg.slice(heightIe);
			//alert( svg.slice(0,20));
			
			//jvector map sanitize
			svg = svg.replace(/<div[^<]+<\/div>/gi, '');
			svg = svg.replace(/<svg /gi, '<svg xmlns="http://www.w3.org/2000/svg"  version="1.1" ');
			svg = svg.replace(/<g><\/g>/gi, '');
			svg = svg.replace(/<g/,'<rect x="0" y="0" width="100%" height="100%" fill="' + mapBackground + '"></rect><g');
			//<text x="20" y="20" font-family="sans-serif" font-size="20px" fill="black">'+ this.titleContainer.text() +'</text>
			// standard sanitize
			svg = svg
				.replace(/zIndex="[^"]+"/g, '')
				.replace(/isShadow="[^"]+"/g, '')
				.replace(/symbolName="[^"]+"/g, '')
				.replace(/jQuery[0-9]+="[^"]+"/g, '')
				.replace(/isTracker="[^"]+"/g, '')
				.replace(/url\([^#]+#/g, 'url(#')
		
				// Replace HTML entities, issue #347
				.replace(/ /g, '\u00A0') // no-break space
				.replace(/­/g,  '\u00AD') // soft hyphen
		
				// IE specific
				.replace(/id=([^" >]+)/g, 'id="$1"')
				.replace(/class=([^" ]+)/g, 'class="$1"')
				.replace(/ transform /g, ' ')
				.replace(/:(path|rect)/g, '$1')
				.replace(/style="([^"]+)"/g, function (s) {
					return s.toLowerCase();
				});
		
			// IE9 beta bugs with innerHTML. Test again with final IE9.
			svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
				.replace(/"/g, "'");
			if ((svg.match(/ xmlns="/g)) && svg.match(/ xmlns="/g).length === 2) {
				svg = svg.replace(/xmlns="[^"]+"/, '');
			}
		
			//console.log(svg);
			var export_url = "/global/scripts/jquery/highcharts/exporting-server/index.php";
			var type = "";
			
			switch(fileType) {
				case 'svg':
					type="image/svg+xml";
					break;
				case 'pdf':
					type="application/pdf";
					break;
				case 'png':
				default:
					type="image/png";
					break;
			}
			
			String.prototype.replaceAll = function(search, replace)
			{
				//if replace is null, return original string otherwise it will
				//replace search string with 'undefined'.
				if(!replace) 
					return this;
			
				return this.replace(new RegExp('[' + search + ']', 'g'), replace);
			};
						
			var hiddenForm=$('<form action="' + export_url+ '" method="POST">' + 
			'<input type="hidden" name="svg" id="hiddensvg">' +
			'<input type="hidden" name="type" value="' + type + '">' +
			'<input type="hidden" name="filename" value="map_of_' + this.titleContainer.text().replaceAll(':',',').replaceAll(' ','_') + '">' +
			'</form>');
			$('body').append(hiddenForm);
			hiddenForm.find('input[id=hiddensvg]').val(svg);
			hiddenForm.submit();
			hiddenForm.remove();
			
		},
		
		__classvars__ : {
			palette : ['#7ce3ff', '#4cc9ff', '#0196d8', '#00628c', '#00355b']
		}
	});
});

null;


/**
 *	The 'continuousMap' module does stuff.
 *
 *	@module continuousMap
 *	@requires signals, map, jVectorMap, usa-en
 */
define('continuousMap',
[
	'signals',
	'map',
	'order!jVectorMap',
	'order!usa-en'
],
function(signals, Map, mapTemplate){
	
	return Map.$extend({
		/**
		 *	Constructor Function for continuousMap
		 *	@method __init__
		 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the continuousMap component.
		 */
		__init__ : function(container, basemap){
			this.$super(container, basemap);
		},
		
		createLegend : function(colors){
			var self = this, min = this,
			minVal, maxVal, prevMinVal, prevMaxVal, displayMin, displayMax;
			this.legendContainer.empty();
			var innerLegendContainer = $('<span style="display: inline-block; text-align:center;"></span>').appendTo(this.legendContainer);
			if (this.options.legendTitle){
				$('<span class="map_legend_title">' + this.options.legendTitle + '</span>').appendTo(innerLegendContainer);
				$('<br />').appendTo(innerLegendContainer);
			}
			if(self.options.legendItemFormatter){
				displayMin = self.options.legendItemFormatter.call(self, this.min);
				displayMax = self.options.legendItemFormatter.call(self, this.max);
			}
			else{
				displayMin = this.min;
				displayMax = this.max;
			}
			self.$class.drawGradient(innerLegendContainer, self.$class.palette[0], self.$class.palette[self.$class.palette.length-1], displayMin, displayMax);
			
			$('<br />').appendTo(innerLegendContainer);
			$('<br />').appendTo(innerLegendContainer);
			var naLegendContainer = $('<div/>').appendTo(innerLegendContainer);
			$('<span/>').appendTo(naLegendContainer).attr('style', 'display:inline-block;margin-right:10px;width:20px;height:20px;background-color:#CCC');
			$('<span>Value is not available</span>').appendTo(naLegendContainer).attr('style', 'margin-top:3px;display:inline-block;');
			
		},
		
		assignColors : function(){
			var ret = [], max = this.max, min = this.min,
			rMin = parseInt(this.$class.palette[0].substring(1, 3), 16),
			rMax = parseInt(this.$class.palette[this.$class.palette.length - 1].substring(1, 3), 16),
			gMin = parseInt(this.$class.palette[0].substring(3, 5), 16),
			gMax = parseInt(this.$class.palette[this.$class.palette.length - 1].substring(3, 5), 16),
			bMin = parseInt(this.$class.palette[0].substring(5), 16),
			bMax = parseInt(this.$class.palette[this.$class.palette.length - 1].substring(5), 16),
			state, count, ratio, r, g, b;
			
			for(var i = 0; i < this.counts.length; i++) {
				for (state in this.counts[i]) {
					if(!ret[i]) ret[i] = {};
					count = parseFloat(Highcharts.numberFormat(this.counts[i][state], this.options.dataPrecision, '.', ''));
					ratio = (count - min) / (max - min);
					switch(this.scaleType) {
						case 'logarithmic':
							ratio = (Math.log(count) - Math.log(min)) / (Math.log(max) - Math.log(min));
							break;
					}
					r = parseInt(ratio * rMax - (ratio - 1) * rMin, 10).toString(16).replace(/^(\w{1})$/, '0$1');
					g = parseInt(ratio * gMax - (ratio - 1) * gMin, 10).toString(16).replace(/^(\w{1})$/, '0$1');
					b = parseInt(ratio * bMax - (ratio - 1) * bMin, 10).toString(16).replace(/^(\w{1})$/, '0$1');
					ret[i][state] = '#' + r + g + b;
				}
			}
			
			this.colors = ret;
		},
		
		__classvars__ : {
			drawGradient : function(container, color1, color2, min, max) {
							
				var gradientText = '';
				
				gradientText += 'background-image: -ms-linear-gradient(left, ' + color1 + ' 0%, ' + color2 + ' 100%);'/* Internet Explorer 10 */
				gradientText += '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=' + color1 + ', endColorstr=' + color2 + ', gradientType=1)";'; /* Internet Explorer 8 */
				gradientText += 'filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=' + color1 + ', endColorstr=' + color2 + ', gradientType=1)'; /* Internet Explorer 5.5 - 7 */
				gradientText += 'background-image: -webkit-gradient(linear, left bottom, right bottom, from(' + color1 + '), to(' + color2 + '));'; /* Safari 4+, Chrome */
				gradientText += 'background-image: -webkit-linear-gradient(left, ' + color1 + ', ' + color2 + ');'; /* Chrome 10+, Safari 5.1+, iOS 5+ */
				gradientText += 'background-image:    -moz-linear-gradient(left, ' + color1 + ', ' + color2 + ');'; /* Firefox 3.6-15 */
				gradientText += 'background-image:      -o-linear-gradient(left, ' + color1 + ', ' + color2 + ';'; /* Opera 11.10+ */
				gradientText += 'background-image:         linear-gradient(to left, ' + color1 + ',' + color2 + ');'; /* Firefox 16+ */
				
				$('<span>' + min + '</span>').appendTo(container);
				$('<span class="map_legend_gradient"></span>').appendTo(container).attr('style', gradientText);
				$('<span>' + max + '</span>').appendTo(container);
				
			}
		}
	});
});

/**
 *	The 'growthMap' module does stuff.
 *
 *	@module growthMap
 *	@requires map
 */
 
define('growthMap',
[
	'map'
],
function(Map){
	
	/**
	 *	Class for the {{#crossLinkModule "growthMap"}}growthMap{{/crossLinkModule}} Module
	 * 	@class GrowthMap
	 *	@extends Map
	 *	@constructor 
	 */
	return Map.$extend({
		/**
		 *	Constructor Function for growthMap
		 *	@method __init__
		 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the growthMap component.
		 */
		__init__ : function(container, basemap){
			this.$super(container, basemap);
		},
		
		update : function(counts, title, options){
			
			counts = $.map(counts, function(count, i){
				var ret = {};
				for(var state in count)
					if(count.hasOwnProperty(state) && i > 0  && typeof counts[i-1] != 'undefined' && counts[i-1].hasOwnProperty(state))
						if((count[state] * counts[i -1][state]) > 0)
							ret[state] = 100 * (count[state] - counts[i-1][state]) / counts[i-1][state];
				return ret;
			});
			
			this.$super(counts, title, 
				$.extend({}, options, {
					backgroundColor : '#ffffff'
				})
			);
		},

		createRegionOps : function(){
			return [{
				attributes : this.getCurrentColors()
			}];
		},
		
		createLegend : function(){
			var self = this,
			minVal, maxVal, prevMinVal, prevMaxVal, displayMin, displayMax;
			this.legendContainer.empty();
			var innerLegendContainer = $('<span style="display: inline-block; text-align:center;"></span>').appendTo(this.legendContainer);
			
			if (this.options.legendTitle){
				$('<span class="map_legend_title">' + this.options.legendTitle + '</span>').appendTo(innerLegendContainer);
				$('<br />').appendTo(innerLegendContainer);
			}
			
			if(self.options.legendItemFormatter){
				displayMin = self.options.legendItemFormatter.call(self, this.min);
				displayMax = self.options.legendItemFormatter.call(self, this.max);
			}
			else{
				displayMin = this.min;
				displayMax = this.max;
			}
				
			var gradientText1 = '';
			var gradientText2 = '';
			
			gradientText1 += 'background-image: -ms-linear-gradient(left, ' + this.$class.palette[0] + ' 0%, ' + this.$class.palette[1] + ' 100%);'/* Internet Explorer 9-11 */
			gradientText1 += '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=' + this.$class.palette[0] + ', endColorstr=' + this.$class.palette[1] + ', gradientType=1)";'; /* Internet Explorer 8 */
			gradientText1 += 'filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=' + this.$class.palette[0] + ', endColorstr=' + this.$class.palette[1] + ', gradientType=1)'; /* Internet Explorer 5.5 - 7 */
			gradientText1 += 'background-image: -webkit-gradient(linear, left bottom, right bottom, from(' + this.$class.palette[0] + '), to(' + this.$class.palette[1] + '));'; /* Safari 4+, Chrome */
			gradientText1 += 'background-image: -webkit-linear-gradient(left, ' + this.$class.palette[0] + ', ' + this.$class.palette[1] + ');'; /* Chrome 10+, Safari 5.1+, iOS 5+ */
			gradientText1 += 'background-image:    -moz-linear-gradient(left, ' + this.$class.palette[0] + ', ' + this.$class.palette[1] + ');'; /* Firefox 3.6-15 */
			gradientText1 += 'background-image:      -o-linear-gradient(left, ' + this.$class.palette[0] + ', ' + this.$class.palette[1] + ';'; /* Opera 11.10+ */
			gradientText1 += 'background-image:         linear-gradient(to left, ' + this.$class.palette[0] + ',' + this.$class.palette[1] + ');'; /* Firefox 16+ */
			
			gradientText2 += 'background-image: -ms-linear-gradient(left, ' + this.$class.palette[3] + ' 0%, ' + this.$class.palette[4] + ' 100%);'/* Internet Explorer 9-11 */
			gradientText2 += '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=' + this.$class.palette[3] + ', endColorstr=' + this.$class.palette[4] + ', gradientType=1)";'; /* Internet Explorer 8 */
			gradientText2 += 'filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=' + this.$class.palette[3] + ', endColorstr=' + this.$class.palette[4] + ', gradientType=1)'; /* Internet Explorer 5.5 - 7 */
			gradientText2 += 'background-image: -webkit-gradient(linear, left bottom, right bottom, from(' + this.$class.palette[3] + '), to(' + this.$class.palette[4] + '));'; /* Safari 4+, Chrome */
			gradientText2 += 'background-image: -webkit-linear-gradient(left, ' + this.$class.palette[3] + ', ' + this.$class.palette[4] + ');'; /* Chrome 10+, Safari 5.1+, iOS 5+ */
			gradientText2 += 'background-image:    -moz-linear-gradient(left, ' + this.$class.palette[3] + ', ' + this.$class.palette[4] + ');'; /* Firefox 3.6-15 */
			gradientText2 += 'background-image:      -o-linear-gradient(left, ' + this.$class.palette[3] + ', ' + this.$class.palette[4] + ';'; /* Opera 11.10+ */
			gradientText2 += 'background-image:         linear-gradient(to left, ' + this.$class.palette[3] + ',' + this.$class.palette[4] + ');'; /* Firefox 16+ */
			
			$('<span>' + displayMin + '</span>').appendTo(innerLegendContainer);
			var gradientContainer = $('<span class="map_legend_gradient"></span>').appendTo(innerLegendContainer);
			$('<span class="growth_gradient_component"></span>').attr('style', gradientText1).appendTo(gradientContainer);
			$('<span class="growth_gradient_component"></span>').attr('style', gradientText2).appendTo(gradientContainer);
			$('<span>' + displayMax + '</span>').appendTo(innerLegendContainer);
			$('<br />').appendTo(innerLegendContainer);
			$('<br />').appendTo(innerLegendContainer);
			var naLegendContainer = $('<div/>').appendTo(innerLegendContainer);
			$('<span></span').appendTo(naLegendContainer).attr('style', 'display:inline-block;margin-right:10px;width:20px;height:20px;background-color:#CCC');
			$('<span>Value is not available</span>').appendTo(naLegendContainer).attr('style', 'margin-top:3px;display:inline-block;');
		},

		calculateMinMax : function() {
			if(this.options.extremes !== void 0){
				this.min = this.options.extremes.min;
				this.max = this.options.extremes.max;
			}
			else{
				this.$super();
			
				if(this.max > 0 && this.min < 0){
					if(Math.abs(this.min) > this.max)
						this.max = Math.abs(this.min);
					else
						this.min = -1 * this.max;
				}	
			}
		},
		
		assignColors : function(){
			var ret = [], max = this.max, min = this.min,
			rMin, rMax, gMin, gMax, bMin, bMax,
			state, count, bucket, ratio, r, g, b;
			
			for(var i = 0; i < this.counts.length; i++){
				for (state in this.counts[i]) {
					count = parseFloat(Highcharts.numberFormat(this.counts[i][state], this.options.dataPrecision, '.', ''));
					if(count < 0){
						ratio = (count - min) / (-1 * min);
						rMin = parseInt(this.$class.palette[0].substring(1, 3), 16);
						rMax = parseInt(this.$class.palette[1].substring(1, 3), 16);
						gMin = parseInt(this.$class.palette[0].substring(3, 5), 16);
						gMax = parseInt(this.$class.palette[1].substring(3, 5), 16);
						bMin = parseInt(this.$class.palette[0].substring(5), 16);
						bMax = parseInt(this.$class.palette[1].substring(5), 16);
						r = (rMin < rMax ? Math.max : Math.min).call(window, parseInt(ratio * rMax - (ratio - 1) * rMin, 10), rMin).toString(16).replace(/^(\w{1})$/, '0$1');
						g = (gMin < gMax ? Math.max : Math.min).call(window, parseInt(ratio * gMax - (ratio - 1) * gMin, 10), gMin).toString(16).replace(/^(\w{1})$/, '0$1');
						b = (bMin < bMax ? Math.max : Math.min).call(window, parseInt(ratio * bMax - (ratio - 1) * bMin, 10), bMin).toString(16).replace(/^(\w{1})$/, '0$1');
					}
					else if(count > 0){
						ratio = count / max;
						rMin = parseInt(this.$class.palette[3].substring(1, 3), 16);
						rMax = parseInt(this.$class.palette[4].substring(1, 3), 16);
						gMin = parseInt(this.$class.palette[3].substring(3, 5), 16);
						gMax = parseInt(this.$class.palette[4].substring(3, 5), 16);
						bMin = parseInt(this.$class.palette[3].substring(5), 16);
						bMax = parseInt(this.$class.palette[4].substring(5), 16);
						r = (rMin > rMax ? Math.max : Math.min).call(window, parseInt(ratio * rMax - (ratio - 1) * rMin, 10), rMax).toString(16).replace(/^(\w{1})$/, '0$1');
						g = (gMin > gMax ? Math.max : Math.min).call(window, parseInt(ratio * gMax - (ratio - 1) * gMin, 10), gMax).toString(16).replace(/^(\w{1})$/, '0$1');
						b = (bMin > bMax ? Math.max : Math.min).call(window, parseInt(ratio * bMax - (ratio - 1) * bMin, 10), bMax).toString(16).replace(/^(\w{1})$/, '0$1');
					}
					else{
						r = this.$class.palette[2].substring(1, 3);
						g = this.$class.palette[2].substring(3, 5);
						b = this.$class.palette[2].substring(5);
					}
					if(!ret[i]) ret[i] = {};
					ret[i][state] = '#' + r + g + b;
				}
			}
			
			this.colors = ret;
		},
		
		__classvars__ : {
			palette : ['#6F2EA4', '#C5ABDB', '#CCCCCC', '#BED5AD', '#5D9732']
		}
	});
});


define('text!templates/animatedMap.html',[],function () { return '<h2 id="mapTitle"></h2>\r\n<div id="mapContainer"></div>\r\n<div id="controls_container">\r\n\t<h2>Map controls & legend</h2>\r\n\t<div class="animated_map_button_container">\r\n\t\t<div id="animated_map_type_container">\r\n\t\t\t<span class="optionsLabel">Value options:</span>\r\n\t\t\t{{#mapTypes}}\r\n\t\t\t<input id="radio_{{id}}" type="radio" name="animated_map_type_radio" value="{{id}}" /><label for="radio_{{id}}">{{title}}</label>\r\n\t\t\t{{/mapTypes}}\r\n\t\t</div>\r\n\t\t<br/>\t\r\n\t\t<div id="animated_map_slider"></div>\r\n\t\t<br/>\r\n\t\t<div id="animated_map_transport_container">\r\n\t\t\t<input type="checkbox" id="play_button"></input><label for="play_button">Play</label>\r\n\t\t\t<input type="checkbox" id="pause_button"></input><label for="pause_button">Pause</label>\r\n\t\t\t<button id="reset_button">Reset</button>\r\n\t\t</div>\r\n\t\t<hr>\r\n\t\t<div id="map_units_label_container">\r\n\t\t\t<span id="map_units_label"></span>\r\n\t\t</div>\r\n\t\t<div id="map_legend"></div>\r\n\t</div>\r\n</div>\r\n';});

define('js/view/VC_AnimatedMap',[
	'classy', 
	'continuousMap', 
	'growthMap', 
	'signals', 
	'js/view/DateCodeUtil', 
	'text!templates/animatedMap.html', 
	'mustache', 
	'us-merc-en'
],function(
	Class, 
	ContinuousMap, 
	GrowthMap, 
	signals, 
	DateCodeUtil, 
	mapTemplate
){
	return Class.$extend({
		__init__ : function(container){
			this.ContinuousMapClass = ContinuousMap = ContinuousMap.$extend({
				__classvars__: {
					palette : ['#b0efff', '#001F37']
				}
			});
			this.GrowthMapClass = GrowthMap;
			
			var self = this;
			
			this.container = container;
			
			this.container.html(Mustache.render(mapTemplate, {
				mapTypes : [
					{id:this.$class.CONTINUOUS_MAP, title:'Values'},
					{id:this.$class.GROWTH_MAP, title:'Growth'}
				]
			}));
			
			this.createPlayerControls();
			
			this.mapTypeButtonset = $('#animated_map_type_container', this.container).buttonset();
			this.mapTypeSelector = $('[name=animated_map_type_radio]', this.container);
			this.unitsLabel = $('#map_units_label', this.container);
			this.mapTypeChangeRequested = new signals.Signal();
			
			this.mapTypeSelectorHandler = function(event){
				self.mapTypeChangeRequested.dispatch(
					self.mapTypeSelector.filter(':checked').val()
				);
			};

		},
		
		createPlayerControls : function() {
			var self = this;
			
			this.playButton = $('#play_button', this.container).button({icons: {primary: "map_play_icon"}});
			this.pauseButton = $('#pause_button', this.container).button({icons: {primary: "map_pause_icon"}});
			this.resetButton = $('#reset_button', this.container).button({icons: {primary: "map_reset_icon"}});
			this.slider = $('#animated_map_slider', this.container).slider();
			this.handle = $('#animated_map_slider', this.container).children('.ui-slider-handle');
			this.sliderLabel = $('<div class="map_slider_tooltip" style="margin-top: -18px;"></div>')
				.css({
					position : 'absolute'
				})
				.appendTo(this.handle);
				
			this.playHandler = function(){
				self.map.stop();
				if(self.position == self.displayColumns[self.displayColumns.length - 1])
					self.map.reset(0);
				self.setButtonState(true);
				self.map.play(false, 300);
			};
			
			this.pauseHandler = function(){
				self.map.stop();
				self.setButtonState(false);
			};
			
			this.resetHandler = function(){
				self.map.stop();
				self.map.reset(self.displayColumns.length-1);
				self.position = self.displayColumns[self.displayColumns.length-1];
				self.map.setTitle(self.title + ' ' + self.displayColumns[self.displayColumns.length-1]);
				self.setButtonState(false);
				$('#reset_button').removeClass('red');
			}
			
			this.sliderHandler = function(event, ui){
				self.map.stop();
				self.map.reset(ui.value);
				self.position = self.displayColumns[ui.value];
				self.setButtonState(false);
			};
			$('label[for="' + this.pauseButton.attr('id') + '"]').hide();
			
		},
		
		destroy : function(){
			if(this.map) this.map.destroy();
		},
		
		update : function(data, mapType, start, end, frequency, options, basemap){
			
			var self = this;
			var newIteration = 0;
			
			this.displayColumns = [];
			this.counts = [];
			
			this.basemap = typeof basemap == 'undefined' ? 'us_merc_en' : basemap;
			
			this.title = data.TITLE;
			this.data = data;
			this.mapType = mapType;
			this.start = start;
			this.end = end;
			this.options = options;
			
			if(this.map){
				this.map.stop();
				this.map.destroy();
			}
			
			this.setButtonState(false);
			
			switch(mapType){
				case this.$class.GROWTH_MAP:
					this.map = new this.GrowthMapClass(this.container, this.basemap);
					this.units = 'Percent change from prior'
					this.precision = 2;
					break;
				default:
					this.map = new this.ContinuousMapClass(this.container, this.basemap);
					this.units = data.UNITS || '';
					this.precision = data.PRECISION;
					break;
			}

			this.unitsLabel.text(this.units);
			
			this.mapTypeSelector.filter('[value=' + mapType + ']').attr('checked', 'true');
			this.mapTypeButtonset.buttonset('refresh');
			
			$.each(data.DATACOLUMNS, function(i, col){
				if(col >= start && col <= end){
					self.displayColumns.push(col);
					self.counts.push(data.DATA[col]);
				}
			});
			
			if(this.position !== void 0){
				newIteration = $.inArray(this.position, this.displayColumns);
				if(!~newIteration)
					newIteration = this.displayColumns.length - 1;
			}
			else{
				newIteration = this.displayColumns.length - 1;
			}
			
			this.map.animation.enabled = true;
			this.map.update(
				this.counts,
				this.title + ' ' + this.displayColumns[newIteration],
				$.extend(
				{
					animation:{
						enabled:true,
						callback : function(iteration){
						
							if(this.colors) {
								this.worldMap.series.regions[0].setValues(this.getCurrentColors());
							}
							else if(this.worldMap.series.regions && this.worldMap.series.regions.length > 0) {
								this.worldMap.series.regions[0].setValues(this.getCurrentCounts());
							}
							var sliderLabelCSS = {};
							var col = self.displayColumns[iteration];
							self.position = col;
							var dateLabel = (typeof col === 'undefined' || col == null) ? '' : DateCodeUtil.formatDateCode(col, frequency);
							self.map.setTitle(self.title + ' ' + dateLabel);
							self.sliderLabel.text(dateLabel);
							sliderLabelCSS.left = -0.5 * self.sliderLabel.width() + 0.5 * self.handle.width();
							if(self.sliderLabel.height() > 1.5 * parseInt(self.sliderLabel.css('font-size').slice(0, -2), 10))
								sliderLabelCSS.top = -0.5 * self.sliderLabel.height()
							else
								sliderLabelCSS.top = 0;
							self.sliderLabel.css(sliderLabelCSS);
							self.slider.slider('option', 'value', iteration);
							if(iteration == self.displayColumns.length - 1)
								self.setButtonState(false);
							
							if(this.basemap == 'coal_basin_merc_en') {
								$('path[data-code^="US-"]', this.container).attr('fill','#666666');
							}
						},
						iteration : newIteration
					},
					continuous : true,
					isGrowth : true,
					dataPrecision : 2,
					extremes : {
						min : -100,
						max : 100
					},
					legendItemFormatter : function(value){
						var ret;

						if(mapType == self.$class.GROWTH_MAP){
							ret = Highcharts.numberFormat(value, self.precision) + '%';
							if(value > 0)
								ret += '+'
						}
						else
							ret = Highcharts.numberFormat(value, self.precision);

						return ret;
					},
					'zoomOnScroll' : true,
					'onRegionLabelShow' : function(event, label, code){
						if (code) {
							var val = self.map.getCurrentCounts()[code];
							if(val !== void 0)
								$(label).html(
									$(label).html() + ' ' +
									Highcharts.numberFormat(val, self.precision) +
									' ' + ((self.mapType == self.$class.GROWTH_MAP) ? '% change from ' + 
									DateCodeUtil.formatDateCode(DateCodeUtil.deincrementDateCode(self.position, frequency), frequency) + ' to ' + 
									DateCodeUtil.formatDateCode(self.position, frequency) : self.units)
								);
						}
						else {
							label.empty();
						}
					}
				}, options)
			);
			
			this.map.reset(newIteration);
			
			this.slider.slider('option', 'min', 0);
			this.slider.slider('option', 'max', this.displayColumns.length - 1);
			this.slider.slider('option', 'value', newIteration);
			
			if(typeof this.sliderHandler == 'function') {
				this.slider.unbind('slide', this.sliderHandler);
			}
			this.slider.bind('slide', this.sliderHandler);
			
			if(typeof this.playHandler == 'function') {
				this.playButton.unbind('click', this.playHandler);
			}
			this.playButton.bind('click', this.playHandler);
			
			if(typeof this.pauseHandler == 'function') {
				this.pauseButton.unbind('click', this.pauseHandler);
			}
			this.pauseButton.bind('click', this.pauseHandler);
			
			if(typeof this.resetHandler == 'function') {
				this.resetButton.unbind('click', this.resetHandler);
			}
			this.resetButton.bind('click', this.resetHandler);
			
			if(typeof this.mapTypeSelectorHandler == 'function') {
				this.mapTypeSelector.unbind('change', this.mapTypeSelectorHandler);
			}
			this.mapTypeSelector.bind('change', this.mapTypeSelectorHandler);
		},
		
		setButtonState : function(playing){
			if(playing){
				this.playButton.attr('checked', false);
				$('label[for="' + this.playButton.attr('id') + '"]').hide();
				$('label[for="' + this.pauseButton.attr('id') + '"]').show();
			}
			if(!playing){
				this.pauseButton.attr('checked', false);
				$('label[for="' + this.pauseButton.attr('id') + '"]').hide();
				$('label[for="' + this.playButton.attr('id') + '"]').show();
			}
			this.playButton.button('refresh');
			this.pauseButton.button('refresh');
		},
		
		outputToArrOfArrs : function(){
			var ret = [],
			keys = [],
			countries = this.map.worldMap.regions,
			key, i, j,
			countryData;
			
			for(key in countries)
				if(countries.hasOwnProperty(key))
					keys.push(key);
					
			keys.sort();
			
			ret.push(['Units: ' + (this.mapType == this.$class.GROWTH_MAP ? 'percent change from previous period' : this.data.UNITS)]);
			var dispCols = this.displayColumns.slice();
			dispCols.unshift('');
			ret.push(dispCols);
			
			for(j=0; j < keys.length; j++){
				key = keys[j]
				ret.push(countryData = ['"' + key + '"']);
				for(i=0; i < this.counts.length; i++){
					countryData.push(this.map.counts[i][key] || '');
				}
			}
			
			return ret;
		},

        embed : function() {
            var embedOptions = this.options.embedOptions;
            var params = [];

            for (var key in embedOptions) {
                if (embedOptions.hasOwnProperty(key)) {
                    params.push(key + "=" + embedOptions[key]);
                }
            }

            window.open('/opendata/embed.php?' + params.join('&'));
        },


        __classvars__ : {
			CONTINUOUS_MAP : '0',
			GROWTH_MAP : '1'
		}
	});
});

define('text!templates/stateCoalBasinSelection.html',[],function () { return '<div id="state_basin_button">\r\n\t<input type="radio" id="state-radio" name="state_basin" value="s"/>\r\n\t<label for="state-radio" alt="U.S. States" title="U.S. States" >U.S. states</label>\r\n\t<input type="radio" id="basin-radio" name="state_basin" value="b"/>\r\n\t<label for="basin-radio" alt="Coal Basins" title="Coal Basins" >Coal basins</label>\r\n</div>';});

define('js/view/VC_StateCoalBasinSelection',[
	'classy',
	'signals',
	'text!templates/stateCoalBasinSelection.html',
],
function(Class, signals, stateCoalBasinSelection)
{
	return Class.$extend({
		
		stateCoalBasinSelect : null,
		
		selectionChanged : null,
		
		__init__ : function(container, config){
			this.container = container;
			
			this.selectionChanged = new signals.Signal();
			
			this.create(config.rtype);
		},
		
		create : function(rtype){
			var self = this;
			this.container.html(
				Mustache.render(stateCoalBasinSelection, {})
			);
			this.stateCoalBasinSelect = $('#state_basin_button', this.container);
			this.stateCoalBasinSelect.buttonset();
			
			$('input', this.stateCoalBasinSelect)
			.change(function(){
				self.selectionChanged.dispatch($('input:checked', self.stateCoalBasinSelect).val());
			});
			
			this.updateSelection(rtype);
		},
		
		updateSelection : function(rtype){
			$('input', this.stateCoalBasinSelect).filter('[value="' + rtype + '"]').attr('checked', true);
			this.stateCoalBasinSelect.buttonset('refresh');
		}
	});
});
/**
 *	The 'qTipsHelp' module does stuff.
 *
 *	@module qTipsHelp
 *	@requires classy, kizzy, qtips
 */
 
define('qTipsHelp',
[
	'classy',
	'signals',
	'qtips'
],
function(
	Class,
	signals
){
	/**
	 *	Class for the {{#crossLinkModule "qTipsHelp"}}qTipsHelp{{/crossLinkModule}} Module
	 * 	@class QTipsHelp
	 *	@extends Class
	 *	@constructor 
	 */
	return Class.$extend({	
		/**
		 *	Constructor Function for qTipsHelp
		 *	@method __init__
		 */
		__init__ : function(){
			var self = this;
			
			this.tooltipsShown = false;
			
			this.button = $('#help_button');
			this.helpButtonClicked = new signals.Signal();
			
			this.button.click( function(e){
				if(!self.button.hasClass('disabled'))
					self.helpButtonClicked.dispatch();
			});
			
			this.windowMouseDownHandler = function(e){
				var isButton = function(buttonEle){
					var ele = e.target;
					while(ele.parentNode){
						if(ele.parentNode == buttonEle)
							return true;
						else
							ele = ele.parentNode;
					}
					return false;
				}(self.button.get(0));
				
				if(self.tooltipsShown && !isButton)
					self.hideHelpTooltips();
			};
		},
		
		toggleHelpTooltips : function(qtips){
			if(this.tooltipsShown)
				this.hideHelpTooltips();
			else
				this.showHelpTooltips(qtips);
		},
		
		showHelpTooltips : function(qtips){
			var self = this;
			this.button.css('cursor', 'wait').addClass('selected');
			// Iterate over each tooltip
			for(var i=0;i<qtips.length;i++){
				var target = ( typeof qtips[i].selector == 'function' ? qtips[i].selector() : $(qtips[i].selector));
				$.each($(target), function(index, item) {
					if($(item).is(':visible')) {
						$(item).attr('class', $(item).attr('class') + ' help_target');
						$(item).qtip({
							content: {prerender: true, text: qtips[i].tip},
							show: 'null',
							hide: 'null',
							style: {
								tip: true,
								// Inherit from preset style
								name: 'cream',
								// Only use the default width of 250px if width is not specified
								width: qtips[i].width ? qtips[i].width : 250
							},
							position: {
								corner: {
									target: qtips[i].position,
									tooltip: self.$class.qtipsOpposite[qtips[i].position]
								},
								adjust: {
									x: qtips[i].xAdjust ? qtips[i].xAdjust : 0,
									y: qtips[i].yAdjust ? qtips[i].yAdjust : 0
								}
							}
						});
					}
				});
			}
			$('.help_target').qtip('show');
			this.button.css('cursor', 'default');
			// We hardcode the width, because it is too much of a pain to
			// determine the appropriate width programatically
			// We use mousedown because many elements capture click events
			$('body').width(Math.max($(window).width(), 1600)).mousedown(this.windowMouseDownHandler);
			$(window).scrollLeft(($('body').width() - $(window).width()) / 2).resize();
			this.tooltipsShown = true;
		},
		
		hideHelpTooltips : function(){
			this.button.removeClass('selected');
			// Fixes IE8 "Black Screen of Death"
			$('.qtip iframe').hide();
			$('.help_target').removeClass('help_target').qtip('destroy');
			$('body').width('100%').scroll().unbind('mousedown', this.windowMouseDownHandler);
			this.tooltipsShown = false;
			$(window).resize();
		},
		
		__classvars__ : {
			qtipsOpposite : {
				topLeft: 'bottomRight',
				topMiddle: 'bottomMiddle',
				topRight: 'bottomLeft',
				bottomLeft: 'topRight',
				bottomMiddle : 'topMiddle',
				bottomRight: 'topLeft',
				leftTop: 'rightBottom',
				leftMiddle: 'rightMiddle',
				leftBottom: 'rightTop',
				rightTop: 'leftBottom',
				rightMiddle: 'leftMiddle',
				rightBottom: 'leftTop',
				center : 'center'
			}
		}
	})
});

/**
 *	The 'viewStack' module does stuff.
 *	@module viewStack
 *	@requires  classy
 */

define('viewStack',
[
	'classy'
], 
function(Class){
	/**
	 *	Class for the {{#crossLinkModule "viewStack"}}viewStack{{/crossLinkModule}} Module
	 * 	@class ViewStack
	 *	@extends Class
	 *	@constructor 
	 */
	return Class.$extend({
		/**
		 *	Constructor Function for viewStack
		 *	@method __init__
		 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the viewStack component.
		 */
		__init__ : function(container){
			this.container = container;
			this.viewNames = [];
			this.viewsMap = {};
		},
		addView : function(name, view){
			if(!view) view = $('<div/>').attr('id', name);
			this.container.append(view);
			this.viewNames.push(name);
			this.viewsMap[name] = view;
			if(this.viewNames.length == 1) this.setActiveView(name);
			else this.positionView(view, false);
			return view;
		},
		removeView : function(name){
			if(this.viewsMap.hasOwnProperty(name)){
				var view = this.viewsMap[name];
				delete this.viewsMap[name];
				this.viewNames.splice($.indexOf(this.viewNames, name), 1);
				view.remove();;
				if(this.activeName == name && this.viewNames.length > 0)
					this.setActiveView(this.viewNames[0]);
			}
		},
		getView : function(name){
			return this.viewsMap[name];
		},
		hasView : function(name){
			return name in this.viewsMap;
		},
		setActiveView : function(name){
			if(name == this.activeName) return;
			if(!(name in this.viewsMap)) throw new Error('Invalid View Name');
			else {
				this.activeName = name;
				for(var key in this.viewsMap){
					if(this.viewsMap.hasOwnProperty(key)){
						var view = this.viewsMap[key];
						this.positionView(view, key == name);
					}
				}
			}
		},
		positionView : function(view, active){
			//var left = top = active ? -9999 : 0;
			view.css({
				display : active ? 'block' : 'none'
			});
		}
	})
});


define('text!templates/tableTitle.html',[],function () { return '<h1>{{title}}</h1>';});


define('text!templates/footnotes.html',[],function () { return '<p>W = Data withheld to prevent disclosure.</p>\r\n{{&footnoteText}}\r\n<p>\r\n\t<a \r\n\t\tclass="lightbox" \r\n\t\tid="termsLink" \r\n\t\thref="data/termsAndDefs.php?{{urlArgs}}">\r\n\t\tTerms and definitions\r\n\t</a>\r\n</p>';});

define('js/pages/CoalAggregateDataPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	//Facade
	'js/Facade',
	'js/Controller',
	//Model
	'js/model/AggregateDataConfigProxy',
	'js/model/AggregateDataRequestProxy',
	'js/model/AggregateDataParameterProxy',
	'js/model/AggregateDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/MapDataProxy',
	'js/model/FootnoteProxy',
	//View
	'js/view/MediatorCoalTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorPinKeyVisualization',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorDualSliderTimeSelection',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorAnimatedMap',
	'js/view/MediatorAnalysis',
	'js/view/MediatorAggregateDataGrid',
	'js/view/MediatorStateCoalBasinSelection',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'js/view/VC_Analysis',
	'js/view/VC_LineChart',
	'js/view/VC_ColumnChart',
	'js/view/VC_AnimatedMap',
	'js/view/VC_StateCoalBasinSelection',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'datagrid',
	'qTipsHelp',
	'viewStack',
	// Templates
	'text!templates/tableTitle.html',
	'text!templates/footnotes.html',
	// JS and CSS
	'simpleModal'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	MapDataProxy,
	FootnoteProxy,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	VisualizationMediator,
	PinKeyVisualizationMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	TimeSelectionMediator,
	HelpTooltipMediator,
	AnimatedMapMediator,
	AnalysisMediator,
	DataGridMediator,
	StateCoalBasinSelectionMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	TimeSelectionDialog,
	AnalysisDialog,
	TopicLineChart,
	TopicColumnChart,
	EnerdatAnimatedMap,
	StateCoalBasinSelection,
	dateCodeUtils,
	GridFormatters,
	DataGrid,
	HelpTooltipDialog,
	ViewStack,
	tableTitleTemplate,
	footnoteTemplate
){
	var facade = null;
	
	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			//testing for error notification email
			//throw {name : "NotImplementedError", message : "too lazy to implement"};

			$('#pagecontent').addClass('aggregate');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = AggregateDataFacade.getInstance(this.name);
				facade.sendNotification(AggregateDataFacade.STARTUP, request);
			}
			else
				facade.sendNotification(AggregateDataFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(AggregateDataFacade.UNLOAD);
			AggregateDataFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('aggregate');
		}
	})('js/pages/CoalAggregateDataPage');
	
	////////////
	// Facade //
	////////////
	
	var AggregateDataFacade = Facade.$extend({
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.CHART_PARAMS_UPDATED, Controller.UpdateChartDataCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, TableSelectionUpdatedCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_DATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.TABLEDATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.DEFAULT_CHART_SET, Controller.UpdateChartedCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.MAP_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.RSE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.AGGREGATE_DATA_CLICK_THROUGH, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.SOURCE_KEY_SELECTED, Controller.OpenAPIWindowCommand);
			this.registerCommand(this.$class.PIN_KEY_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.REGION_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		
		__classvars__ : {
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new AggregateDataFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableParameter = {
				id : 'csv_table',
				type : 'csv',
				'class' : 'ico_csv',
				target : 'table',
				label : 'Table (CSV)'
			};
			var configProxy = new ConfigProxy();
			configProxy.setVisualizations(VisualizationDialog.defaultConfig.items.slice());
			configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
			configProxy.setDownloadParameters(
				{
					'map' : [
						{
							label : 'Map',
							children : [
								{
									id : 'map_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'map',
									label : 'Map (PNG)'
								},
                                {
                                    id : 'map_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'map',
                                    label : 'Map (PDF)'
                                },
                                {
                                    id : 'map_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'map',
                                    label : 'Map (SVG)'
                                },
                                {
                                    id : 'map_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'map',
                                    label : 'Map Embed'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'map_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'map',
									label : 'Map (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'columnchart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'columnchart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'columnchart',
									label : 'PDF'
								},
								{
									id : 'columnchart_png',
									type : 'png',
									'class': 'ico_img',
									target : 'columnchart',
									label : 'PNG'
								},
                                {
                                    id : 'columnchart_svg',
                                    type : 'svg',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'columnchart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'columnchart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'columnchart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'columnchart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'linechart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'linechart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'linechart',
									label : 'PDF'
								},
								{
									id : 'linechart_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'linechart',
									label : 'PNG'
								},
                                {
                                    id : 'linechart_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'linechart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'linechart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'linechart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'linechart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					]
				}
			);
			var requestProxy = new RequestProxy(note.getBody());
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var mapDataProxy = new MapDataProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(mapDataProxy);
			this.facade.registerProxy(footnoteProxy);
			
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TableSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			//this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			//this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	var ChartDataUpdatedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
			
			if(parameterProxy.getChartType() == 'map'){
				mapDataProxy.updateMapData(tableDataProxy.getChartData('map')[0]);
			}
		}
	});

	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this; 
			
			var pageLayout = this.getViewComponent();

			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), {items : configProxy.getVisualizations()});
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			//var analysisDialog = new AnalysisDialog(pageLayout.getAnalysisContainer());
			var dataGrid = new DataGrid(pageLayout.getTableContainer());
			var viewStack = new ViewStack(pageLayout.getChartContainer());
			var pinKeyVisualizationDialog = new VisualizationDialog(pageLayout.getPinKeyVisualizationContainer(), {items : configProxy.getLinkTypes()});
			
			var lineChartContainer = viewStack.addView('linechart');
			var columnChartContainer = viewStack.addView('columnchart');
			var mapContainer = viewStack.addView('map');
			
			var columnChart = new TopicColumnChart(columnChartContainer);
			var lineChart = new TopicLineChart(lineChartContainer);
			var helpTooltipDialog = new HelpTooltipDialog();
			var animatedMap = new EnerdatAnimatedMap(mapContainer);
			
			var formMediator = new FormMediator(formDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var topTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var viewStackMediator = new ViewStackMediator(viewStack);
			var lineChartMediator = new LineChartMediator(lineChart);
			var columnChartMediator = new ColumnChartMediator(columnChart);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var animatedMapMediator = new AnimatedMapMediator(animatedMap);
			//var analysisMediator = new AnalysisMediator(analysisDialog);
			var pinKeyVisualizationMediator = new PinKeyVisualizationMediator(pinKeyVisualizationDialog);
			
			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(formMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(animatedMapMediator);
			//this.facade.registerMediator(analysisMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);
			
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : false,
				showTransportationMode : false,
				showPrimeMovers : false,
				showPlantFuelTypes : false,
				showMineType : false,
				showMineStatus : false
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}))
				.on('change', '#rse_checkbox', function(e){
					self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
						'rse' : $(e.target).is(':checked') ? '1' : '0'
					});
				});
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				AggregateDataFacade.TABLEDATA_UPDATED,
				AggregateDataFacade.RSE_PARAM_UPDATED,
				AggregateDataFacade.TABLE_RENDERED,
				AggregateDataFacade.TOPIC_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);

			switch(note.getName()){
				case AggregateDataFacade.TABLEDATA_UPDATED:
					var pageTitle = tableDataProxy.getTableTitle();
					if(parameterProxy.getReportIndex() != null) {
						pageTitle = configProxy.getReports()[parameterProxy.getReportIndex()].description;
					}
					
					window.document.title = 'Coal Data Browser - ' + pageTitle;
					break;
				case AggregateDataFacade.TABLE_RENDERED:
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				case AggregateDataFacade.RSE_PARAM_UPDATED:
					vc.getFootnoteContainer()
						.find('#rse_checkbox')
						.attr('checked', parameterProxy.getShowRSE()); 
					break;
				case AggregateDataFacade.TOPIC_PARAMS_UPDATED:
					var self = this;
					var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, {
							urlArgs : footnoteProxy.getUrlArgs(), 
							footnoteText : footnoteProxy.getFootnotes()
						}))
						.on('change', '#rse_checkbox', function(e){
							self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
								'rse' : $(e.target).is(':checked') ? '1' : '0'
							});
						});
						
					$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
					break;
				default:
					this.$super(note);
					break;
			}
		},

		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});
	

	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainVisualizationButtonOpts' : {
					'selected' : false,
					'id' : 'main_visualization_button'
				},
				'overlayVisualizationButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_visualization_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
		}
	});
	return page;
});

define('js/model/ImportExportConfigProxy',[
	'js/model/AggregateDataConfigProxy',
	'text!importExportConfig'
], function(
	AggregateDataConfigProxy,
	importExportConfig
){
	return AggregateDataConfigProxy.$extend({
		__init__ : function(){
			this.$super();
			var config = $.parseJSON(importExportConfig);
			this.setPorts(config.PORTS);
			this.setCountries(config.COUNTRIES);
		},
		
		getPorts : function() {
			return this.getData()['PORTS'];
		},
		
		setPorts : function(ports) {
			this.getData()['PORTS'] = ports;
		},
		getCountries : function() {
			return this.getData()['COUNTRIES'];
		},
		
		setCountries : function(countries) {
			this.getData()['COUNTRIES'] = countries;
		}
	});
});
define('js/model/ImportExportMapDataProxy',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy
){
	return puremvc.Proxy.$extend({
		__init__ : function(){
			this.$super(this.$class.NAME);
		},
		
		updateMapData : function(){
			var data = {};
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			
			var topic = configProxy.getTopic(parameterProxy.getTopicId());
			var aggArr = parameterProxy.getAgg().split(',');
			var charted = parameterProxy.getCharted("map");
			var coalRankSeriesIdOrder;
			var ports = {}, countries = {};
			
			this.portSelected = false;
			this.countrySelected = false;
			if(typeof charted != 'undefined') {
				for(var j = 0; j < topic.aggColumns.length; j++) {
					if(topic.aggColumns[j].filterGroup.varName == 'rank') {
						coalRankSeriesIdOrder = topic.aggColumns[j].seriesIdOrder;
					}
				}
				var coalrank = charted.split('.')[2].split('-')[coalRankSeriesIdOrder];
				var units = "";
				var tableData = tableDataProxy.getTableData();
				var coalRankName;
				var columns = tableDataProxy.getDataColumns();
				for(var i = 0; i < columns.length; i++) {
					data[columns[i]] = {};
				}
				
				// Pivot table data for use by the map view components.
				for(var i = 0; i < tableData.length; i++) {
					var seriesFilters = tableData[i].SERIES_ID.split('.')[2].split('-');
					var aggArr = tableData[i].agg.split(",");
					var mapFieldSeriesIdOrder = topic.aggColumns[aggArr[1]].seriesIdOrder;
					
					
					if(tableData[i].HAS_DATA && seriesFilters[coalRankSeriesIdOrder] == coalrank) {
						units = tableData[i].UNITS;
						coalRankName = tableData[i].DESCRIPTION;
						// Create arrays of the ports or countries that have data for them
						if(topic.aggColumns[aggArr[0]].filterGroup.varName == 'cust') {
							this.portSelected = true;
							countries[seriesFilters[mapFieldSeriesIdOrder]] = "";
							ports[seriesFilters[topic.aggColumns[aggArr[0]].seriesIdOrder]] = "";
							$.each(tableData[i].DATA, function(key, value) {
								if(typeof data[key] != 'undefined' && !isNaN(parseFloat(value)) && isFinite(value)) {
									data[key][seriesFilters[mapFieldSeriesIdOrder]] = value;
								}
							});
						}
						else if(topic.aggColumns[aggArr[0]].filterGroup.varName == 'cntry') {
							this.countrySelected = true;
							ports[seriesFilters[mapFieldSeriesIdOrder]] = "";
							countries[seriesFilters[topic.aggColumns[aggArr[0]].seriesIdOrder]] = "";
							$.each(tableData[i].DATA, function(key, value) {
								if(typeof data[key] != 'undefined' && !isNaN(parseFloat(value)) && isFinite(value)) {
									data[key][seriesFilters[mapFieldSeriesIdOrder]] = value;
								}
							});
						}
					}
				}
				var portKeys = [], countryKeys = [];
				$.each(ports, function(key, val) {
					portKeys.push(key);
				});
				$.each(countries, function(key, val) {
					countryKeys.push(key);
				});
				if(typeof coalRankName != "undefined"){
					this.setData({"DATACOLUMNS": columns, "DATA": data, "PORTS" : portKeys, "COUNTRIES" : countryKeys, "TITLE": tableDataProxy.getTableTitle() + " of " + coalRankName, "UNITS":units});
				} else {
					this.setData({"DATACOLUMNS": columns, "DATA": data, "PORTS" : portKeys, "COUNTRIES" : countryKeys, "TITLE": tableDataProxy.getTableTitle(), "UNITS":units});
				}
				this.hasData = true;
				this.sendNotification(Facade.MAP_DATA_UPDATED);
			}
			else {
				this.hasData = false;
				this.sendNotification(Facade.MAP_DATA_UPDATED);
			}
		},
		
		__classvars__ : {		
			NAME : 'MapDataProxy'
		}
	});
});
	
define('js/model/ImportExportAggregateDataTableDataProxy',[
	'js/Facade',
	'js/model/AggregateDataTableDataProxy',
	'js/model/ImportExportMapDataProxy',
	'js/model/ParameterProxy',
	'js/model/RequestProxy',
	'js/model/ConfigProxy'
], function(
	Facade,
	TableDataProxy,
	MapDataProxy,
	ParameterProxy,
	RequestProxy,
	ConfigProxy
){
	return TableDataProxy.$extend({
		onRegister : function() {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var topic = configProxy.getTopic(requestProxy.getTopicId());
			var data = {'all':{},'total':{}};
			
			for(var i = 0; i < topic.aggColumns.length; i++) {
				var filterGroup = topic.aggColumns[i].filterGroup;
				
				var filterValues = {'cust' : [], 'cntry' : [], 'rank' : []}, filterValuesTotal = {'cust' : [], 'cntry' : [], 'rank' : []};
				var maxFilterValueId = 0;
				
				for(var j = 0; j < filterGroup.filterValues.length; j++) {
					maxFilterValueId = Math.max(maxFilterValueId, filterGroup.filterValues[j].valueId);
					if(filterGroup.filterValues[j].filterValue == 'TOT') {
						filterValuesTotal[filterGroup.varName][filterGroup.filterValues[j].valueId] = 1;
					}
					else {
						filterValuesTotal[filterGroup.varName][filterGroup.filterValues[j].valueId] = 0;
					}
					filterValues[filterGroup.varName][filterGroup.filterValues[j].valueId] = 1;
				}
				
				for(var k = 0; k < (maxFilterValueId + (5-(maxFilterValueId%5))); k++) {
					if(typeof filterValues[filterGroup.varName][k] == 'undefined') {
						filterValues[filterGroup.varName][k] = 0;
					}
					if(typeof filterValuesTotal[filterGroup.varName][k] == 'undefined') {
						filterValuesTotal[filterGroup.varName][k] = 0;
					}
				}
				
				var str = data['all'][filterGroup.varName] = '';
				for(var l = 0; l < filterValues[filterGroup.varName].length; l++) {
					str += filterValues[filterGroup.varName][l].toString();
					if(str.length == 5) {
						data['all'][filterGroup.varName] += parseInt(str, 2).toString(32);
						str = "";
					}
				}
				str = data['total'][filterGroup.varName] = '';
				for(var m = 0; m < filterValuesTotal[filterGroup.varName].length; m++) {
					str += filterValuesTotal[filterGroup.varName][m].toString();
					if(str.length == 5) {
						data['total'][filterGroup.varName] += parseInt(str, 2).toString(32);
						str = "";
					}
				}
			}
			
			this.defaults = data;
		},
		
		getTableTitle : function(){
			var mdp = this.facade.retrieveProxy(MapDataProxy.NAME);
			var tableTitle = this.$super();
			var tableSplit = tableTitle.split(' ');
			var impExp = tableSplit[0];
			var totalCheck = tableSplit[3] + ' ' + tableSplit[4];
			if(totalCheck.toLowerCase() == "total world"){
				if(impExp == "Import"){
					tableTitle = tableTitle.replace(" for "," from ");
				} else {
					tableTitle = tableTitle.replace(" for "," to ");
				}
			} else if (mdp.portSelected){
				if (impExp == "Import"){
					tableTitle = tableTitle.replace(" for "," to ");
				} else if (impExp == "Export") {
					tableTitle = tableTitle.replace(" for "," from ");
				}
			} else {
				if (impExp == "Import"){
					tableTitle = tableTitle.replace(" for "," from ");
				} else if (impExp == "Export") {
					tableTitle = tableTitle.replace(" for "," to ");
				}
			}	

			return tableTitle;
		},
		
		loadTableData : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableParamsString = parameterProxy.getTableParamsString(),
			tableParams = parameterProxy.getTableParams(),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			self = this;
			this.isUpdating = true;
			
			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
				this.processChartData();
				this.createIDMap();
				this.isUpdating = false;
				this.sendNotification(Facade.TABLEDATA_UPDATED);
			}
			else {
				if(typeof tableParams['cntry'] == 'undefined' && typeof tableParams['cust'] == 'undefined') {
					var combinedData = null;
					
					parameterProxy.getQuery()['agg'] = '1,2,0';
					tableParamsString = parameterProxy.getTableParamsString();
					$.ajax(
						encodeURI(
							this.$class.PROXY_URL + '?method=' +
							this.$class.PROXY_METHOD + '&' + tableParamsString + 
								"&cntry=" + this.defaults['total']['cntry'] + 
								"&cust=" + this.defaults['all']['cust']
						),
						{
							dataType : 'json',
							error : function(){
								self.sendNotification(Facade.ERROR);
							},
							success : function(data, textStatus, jqXHR) {
								$.each(data.TABLEDATA.ROWS.concat(data.SERIESDATA.DATA), function() {
									this.filterQuery = configProxy.getFilterQuery(parameterProxy.getTopicId(), this.SERIES_ID);
									this.agg = '1,2,0';
								});
								// Wrap the data array in our TableData class
								data.TABLEDATA.ROWS = $.extend(data.TABLEDATA.ROWS, {
									'getRowMetadata' : self.$class.getRowMetadata
								});
								if(combinedData == null) {
									combinedData = data;
								}
								else {
									combinedData.TABLEDATA.ROWS = combinedData.TABLEDATA.ROWS.concat(data.TABLEDATA.ROWS);
									combinedData.chartData = {};
									self.setData(tableParamsString, combinedData);
									self.createIDMap();
									self.processLevels();
									self.processChartData('linechart');
									self.processChartData('columnchart');
									self.processChartData('map');
									self.processChartData('pin');
									self.isUpdating = false;
									self.sendNotification(Facade.TABLEDATA_UPDATED);
								}
							}
						}
					);
					parameterProxy.getQuery()['agg'] = '2,1,0';
					tableParamsString = parameterProxy.getTableParamsString();
					$.ajax(
						encodeURI(
							this.$class.PROXY_URL + '?method=' +
							this.$class.PROXY_METHOD + '&' + tableParamsString + 
								"&cntry=" + this.defaults['all']['cntry'] + 
								"&cust=" + this.defaults['total']['cust']
						),
						{
							dataType : 'json',
							error : function(){
								self.sendNotification(Facade.ERROR);
							},
							success : function(data, textStatus, jqXHR) {
								$.each(data.TABLEDATA.ROWS.concat(data.SERIESDATA.DATA), function() {
									this.filterQuery = configProxy.getFilterQuery(parameterProxy.getTopicId(), this.SERIES_ID);
									this.agg = '2,1,0';
								});
								// Wrap the data array in our TableData class
								data.TABLEDATA.ROWS = $.extend(data.TABLEDATA.ROWS, {
									'getRowMetadata' : self.$class.getRowMetadata
								});
								if(combinedData == null) {
									combinedData = data;
								}
								else {
									combinedData.TABLEDATA.ROWS = data.TABLEDATA.ROWS.concat(combinedData.TABLEDATA.ROWS);
									combinedData.chartData = {};
									self.setData(tableParamsString, combinedData);
									self.createIDMap();
									self.processLevels();
									self.processChartData('linechart');
									self.processChartData('columnchart');
									self.processChartData('map');
									self.processChartData('pin');
									self.isUpdating = false;
									self.sendNotification(Facade.TABLEDATA_UPDATED);
								}
							}
						}
					);
				}
				else{
					$.ajax(
						encodeURI(
							this.$class.PROXY_URL + '?method=' +
							this.$class.PROXY_METHOD + '&' + tableParamsString
						),
						{
							dataType : 'json',
							error : function(){
								self.sendNotification(Facade.ERROR);
							},
							success : function(data){
								$.each(data.TABLEDATA.ROWS.concat(data.SERIESDATA.DATA), function() {
									this.filterQuery = configProxy.getFilterQuery(parameterProxy.getTopicId(), this.SERIES_ID);
									this.agg = parameterProxy.getAgg();
								});
								// Wrap the data array in our TableData class
								data.TABLEDATA.ROWS = $.extend(data.TABLEDATA.ROWS, {
									'getRowMetadata' : self.$class.getRowMetadata
								});
								data.chartData = {};
								self.setData(tableParamsString, data);
								self.createIDMap();
								self.processLevels();
								self.processChartData('linechart');
								self.processChartData('columnchart');
								self.processChartData('map');
								self.processChartData('pin');
								self.isUpdating = false;
								self.sendNotification(Facade.TABLEDATA_UPDATED);
							}
						}
					);
				}
			}
		},
	
		getMappedSeries : function(seriesId, idMap) {
		var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var topic = configProxy.getTopic(parameterProxy.getTopicId());
			
			var ret = [];
			
			var seriesIdArr = seriesId.split('.');
			var filterValArr = seriesIdArr[2].split('-');
			
			$.each(topic.aggColumns, function(index, value) {
				if(value.filterGroup.varName != 'rank') {
					filterValArr[index] = '[A-Za-z_]+';
				}
			});
			
			seriesIdArr[1] = topic.dataType;
			seriesIdArr[2] = filterValArr.join('\\-');
			
			var regex = new RegExp(seriesIdArr.join('\\.'));
			
			$.each(idMap, function(key, value) {
				if(key.match(regex)) {
					ret = ret.concat(value);
				}
			});
			
			return ret;
		}
	});
});
define('js/view/MediatorImportExportForm',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().formSubmitted.add(function(params){
				self.getViewComponent().hideForm();
				self.sendNotification(Facade.TABLE_SELECTION_UPDATED, params, 'FormSubmitted');
			});
			this.getViewComponent().formCanceled.add(function(params){
				self.getViewComponent().hideForm();
			});
			this.getViewComponent().formButtonClicked.add(function(params){
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME),
				configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				var topic = {};
				$.extend(true, topic, configProxy.getTopic(parameterProxy.getTopicId()));
				topic.aggColumns.splice(1,2);
				self.getViewComponent().showForm(
					topic,
					parameterProxy.getFormParams()
				);
			})
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.RELOAD
			]);
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.RELOAD:
					this.getViewComponent().hideForm();
					break;
				default:
					this.$super.handleNotification(note);
			}
		},
		
		__classvars__ : {
			NAME : 'FormMediator'
		}
	});
});
define('js/view/MediatorImportExportMap',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/MapDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/view/MediatorAnimatedMap',
	'signals'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	MapDataProxy,
	ConfigProxy,
	ParameterProxy,
	MediatorAnimatedMap,
	Signals
) {
	return MediatorAnimatedMap.$extend({
		__init__ : function(viewComponent){
			this.$super(viewComponent, this.$class.NAME); 
		},
		
		onRegister : function(){
			var vc = this.getViewComponent();
			var self = this;
			
			var buildQueryString = function(topic, varName, code) {
				var ret = "";
				
				for(var i = 0; i < topic.aggColumns.length; i++) {
					var filterGroup = topic.aggColumns[i].filterGroup;
					
					var filterValues = [];
					var maxFilterValueId = 0;
					
					if(filterGroup.varName == varName) {
						for(var j = 0; j < filterGroup.filterValues.length; j++) {
							maxFilterValueId = Math.max(maxFilterValueId, filterGroup.filterValues[j].valueId);
							if(filterGroup.filterValues[j].filterValue == code) {
								filterValues[filterGroup.filterValues[j].valueId] = 1;
							}
						}
						
						for(var k = 0; k < (maxFilterValueId + (5-(maxFilterValueId%5))); k++) {
							if(typeof filterValues[k] == 'undefined') {
								filterValues[k] = 0;
							}
						}
						
						ret = "";
						var str = "";
						for(var l = 0; l < filterValues.length; l++) {
							str += filterValues[l].toString();
							if(str.length == 5) {
								ret += parseInt(str, 2).toString(32);
								str = "";
							}
						}
						break;
					}
				}
				return ret;
			};
			
			vc.portSelected.add(function(portCode) {
				var configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var topic = configProxy.getTopic(parameterProxy.getTopicId());
				var tableDataProxy = self.facade.retrieveProxy(TableDataProxy.NAME);
				
				var agg = [], query = {};
				for(var i = 0; i < topic.aggColumns.length; i++) {
					switch(topic.aggColumns[i].filterGroup.varName) {
						case 'cust':
							agg[0] = i;
							break;
						case 'cntry':
							agg[1] = i;
							break;
						case 'rank':
							agg[2] = i;
							break;
					}
				}
				query['agg'] = agg.join(',');
				query['cust'] = buildQueryString(topic, 'cust', portCode);
				query['cntry'] = tableDataProxy.defaults['all']['cntry'];
				self.sendNotification(Facade.TABLE_SELECTION_UPDATED, query, 'PortClicked');
			});
				
			vc.countrySelected.add(function(countryCode) {
				var configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var topic = configProxy.getTopic(parameterProxy.getTopicId());
				var tableDataProxy = self.facade.retrieveProxy(TableDataProxy.NAME);
				
				var agg = [], query = {};
				for(var i = 0; i < topic.aggColumns.length; i++) {
					switch(topic.aggColumns[i].filterGroup.varName) {
						case 'cntry':
							agg[0] = i;
							break;
						case 'cust':
							agg[1] = i;
							break;
						case 'rank':
							agg[2] = i;
							break;
					}
				}
				
				query['agg'] = agg.join(',');
				query['cust'] = tableDataProxy.defaults['all']['cust'];
				query['cntry'] = buildQueryString(topic, 'cntry', countryCode);
				
				var validCountry = true;
				if(parseInt(query['cntry'] , 32) == 0) {
					
					validCountry = false;
				}
				if(validCountry) {
					self.sendNotification(Facade.TABLE_SELECTION_UPDATED, query, 'CountryClicked');
				}
			});

			vc.totalSelected.add(function(totalId){
				var query = {};
				var configProxy = self.facade.retrieveProxy(ConfigProxy.NAME);
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				//var topic = configProxy.getTopic(parameterProxy.getTopicId());
				query['cust'] = ''; 
				query['cntry'] = '';
				self.sendNotification(Facade.TABLE_SELECTION_UPDATED, query, 'SelectionChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([]);
		},
		
		handleNotification : function(note){
			switch(note.name){
				case Facade.TIME_PARAMS_UPDATED:
				case Facade.MAP_DATA_UPDATED:
				case Facade.MAP_TYPE_PARAM_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
					var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var topic = configProxy.getTopic(parameterProxy.getTopicId());
					mapDataProxy.getData();
					if(topic.drillDownTopicId != "") {
						this.viewComponent.container.addClass('clickable-regions ').removeClass('clickable-markers');
					}
					else {
						this.viewComponent.container.removeClass('clickable-regions ').removeClass('clickable-markers');
					}
					
					if(!tableDataProxy.isUpdating && mapDataProxy.hasData){
						var self = this,
						timeParams = parameterProxy.getTimeParams(),
						data = mapDataProxy.getData();
						data.COUNTRY_SELECTED = mapDataProxy.countrySelected;
						data.PORT_SELECTED = mapDataProxy.portSelected;
                        var embedOptions = {
                            //'geoset_id' : tableDataProxy.getChartData('map')[0].GEOSET_ID,
                            'geoset_id' : tableDataProxy.getChartData('map')[0] ? tableDataProxy.getChartData('map')[0].GEOSET_ID : '',
                            'type' : 'map',
                            'date_mode' : 'all'
                        };

                        var minPeriod = function(t,f){
                            switch(f){
                                case 'A':
                                    return t.annualStart;
                                case 'M':
                                    return t.monthlyStart;
                                case 'Q':
                                    return t.quarterlyStart;
                            }
                        }(topic, timeParams.freq);

                        var maxPeriod = function(t,f){
                            switch(f){
                                case 'A':
                                    return t.annualEnd;
                                case 'M':
                                    return t.monthlyEnd;
                                case 'Q':
                                    return t.quarterlyEnd;
                            }
                        }(topic, timeParams.freq);

                        if(timeParams.end != maxPeriod) {
                            embedOptions['date_mode'] = 'range';
                            embedOptions['start'] = (timeParams.freq == 'Q') ? timeParams.start.toString().substr(1, 1) + 'Q' + timeParams.start.toString().substr(2, 4) : timeParams.start.toString();
                            embedOptions['end'] = (timeParams.freq == 'Q') ? timeParams.end.toString().substr(1, 1) + 'Q' + timeParams.end.toString().substr(2, 4) : timeParams.end.toString();
                        }
                        else if(timeParams.start != minPeriod) {
                            embedOptions['date_mode'] = 'start';
                            embedOptions['start'] = (timeParams.freq == 'Q') ? timeParams.start.toString().substr(1, 1) + 'Q' + timeParams.start.toString().substr(2, 4) : timeParams.start.toString();
                        }
						
						this.viewComponent.update(
							data,
							parameterProxy.getMapType(),
							timeParams.start,
							timeParams.end,
							timeParams.freq,
							{ 
							
								'regionStyle' : {
									hover : {
										stroke : '#FFFF96',
										"fill-opacity" : 1,
										"stroke-width" : 3,
										"stroke-opacity" : 1
									},
									selected : {
										fill : '#CA0020'
									}
								},
                                'embedOptions' : embedOptions
							}
						);
					}
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'map'){
                        switch(note.body.type) {
                            case 'csv':
                                var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
                                var mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
                                data = mapDataProxy.getData();
                                var csvData = [
                                    ['"' + data.TITLE + '"'],
                                    ['"' + window.location.href + '"'],
                                    ['"' + Date() + '"'],
                                    ['"Source: U.S. Energy Information Administration"']
                                ].concat(this.getViewComponent().outputToArrOfArrs());
                                this.sendNotification(Facade.CSV_DATA_GENERATED, csvData);
                                break;
                            case 'png':
                            case 'pdf':
                                this.getViewComponent().map.exportMap(note.body.type);
                                break;
                            case 'svg':
                                if(window.SVGAngle)
                                    this.getViewComponent().map.exportMap(note.body.type);
                                else
                                    alert("Map download requires a modern browser, such as Chrome, Firefox or Internet Explorer 9+");
                                break;
                            case 'embed':
                                this.getViewComponent().embed();
                                break;
                        }
					}
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				default:
					this.$super(note);
			}
		},
		
		
		onRemove : function() {
			var vc = this.getViewComponent();
			var self = this;
			
			vc.portSelected.removeAll();
			vc.countrySelected.removeAll();
			
			this.$super();
			this.getViewComponent().destroy();
		},
		
		__classvars : {
			NAME : 'MediatorImportExportMap'
		}
	});
});
define('js/view/MediatorImportExportDataGrid',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/view/MediatorAggregateDataGrid',
], function(
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	MediatorAggregateDataGrid
){
	return MediatorAggregateDataGrid.$extend({
		dataGridClick : function(row, cell, datum){
			var vc = this.getViewComponent(), columns = vc.columns;
			var chartType = columns[cell].iconClass;
			
			if(columns[cell].id == 'chart' && !datum.ACTION && chartType == 'map') {
				var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
				configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
				requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
				chartType = parameterProxy.getChartType();
				
				var topic = configProxy.getTopic(requestProxy.getTopicId());
				
				if(datum.HAS_DATA){
					var chartedObj = {};
					
					chartedObj[chartType] = datum.SERIES_ID;
					this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
				}
			}
			else {
				this.$super(row,cell,datum);
			}
		}
	});
});
define('js/view/MediatorImportExportSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent, top){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().selectionChanged.add(function(topicId){
				self.sendNotification(Facade.TOPIC_SELECTION_UPDATED, {'topic' : topicId}, 'TopicChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLE_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			switch(note.getName()){
				case Facade.TABLE_PARAMS_UPDATED:
					this.getViewComponent().create(
						parameterProxy.getTopicId(),
						parameterProxy.getQuery()
					);
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'ImportExportSelectionMediator'
		}
	});
});

define('text!templates/importExportMap.html',[],function () { return '<h2 id="mapTitle"></h2>\r\n<h4 id="import_export_instruct">Click on a country or port to filter.</h4>\r\n<div id="mapContainer"></div>\r\n<div id="import_export_controls_container">\r\n\t<h2>Map controls & legend</h2>\r\n\t<div id="map_us"></div>\r\n\t<div class="animated_map_button_container">\r\n\t\t<div id="animated_map_slider"></div>\r\n\t\t<br/>\r\n\t\t<div id="animated_map_transport_container">\r\n\t\t\t<input type="checkbox" id="play_button"></input><label for="play_button">Play</label>\r\n\t\t\t<input type="checkbox" id="pause_button"></input><label for="pause_button">Pause</label>\r\n\t\t\t<button id="reset_button">Reset</button>\r\n\t\t</div>\r\n\t\t<hr>\r\n\t\t<div id="map_units_label_container">\r\n\t\t\t<span id="map_units_label"></span>\r\n\t\t</div>\r\n\t\t<div id="map_legend"></div>\r\n\t</div>\r\n</div>\r\n<span id="source">Source: U.S. Energy Information Administration; Data Source: U.S. Census Bureau</span>\r\n\r\n\r\n\r\n';});

define('js/view/VC_ImportExportMap',[
	'classy', 
	'continuousMap', 
	'growthMap', 
	'signals', 
	'js/view/DateCodeUtil',
	'js/view/VC_AnimatedMap', 
	'text!templates/importExportMap.html', 
	'mustache', 
	'us-merc-en',
	'world-mill-en',
	'us-terr-merc-en'
],function(
	Class, 
	ContinuousMap, 
	GrowthMap, 
	signals, 
	DateCodeUtil,
	VC_AnimatedMap,
	mapTemplate
){
	return VC_AnimatedMap.$extend({
		__init__ : function(container, config){
			ContinuousMap = ContinuousMap.$extend({
				calculateMinMax : function() {
					this.min = this.max = null;
					for (var i = 0; i < this.counts.length; i++) {
						for (var state in this.counts[i]) {
							if(this.counts[i].hasOwnProperty(state) && state != 'TOT'){
								this.min = ((this.min == null || this.counts[i][state] < this.min) ? this.counts[i][state] : this.min);
								this.max = ((this.max == null || this.counts[i][state] > this.max) ? this.counts[i][state] : this.max);
							}
						}
					}

					if(this.min !== null)
						this.min = parseFloat(Highcharts.numberFormat(this.min, this.options.dataPrecision, '.', ''));
					if(this.max !== null)
						this.max = parseFloat(Highcharts.numberFormat(this.max, this.options.dataPrecision, '.', ''));
				},
				
				__classvars__: {
					palette : ['#b0efff', '#001F37']
				}
			});
			var self = this;
			
			this.config = config;
			
			this.container = container;
			
			this.container.html(Mustache.render(mapTemplate, {}));
			
			this.selectedPort = null;
			this.selectedCountry = null;
			this.portsCon = [];
			for(var z in self.config.ports){
				if(self.config.ports[z].id != "TOT" && self.config.ports[z].id != "OTH"){
					this.portsCon.push(self.config.ports[z]);
				}
			}
			this.usMapContainer = $('#map_us', this.container);
			this.worldMapContainer = $('#mapContainer', this.container);
			this.worldMapContainer.addClass('clickable-regions');
			this.createUSMap();
			//this.createWorldMap();
			this.map_us = $('#map_us').vectorMap('get', 'mapObject');

			this.createPlayerControls();
			this.mapTypeSelector = $('[name=animated_map_type_radio]', this.container);
			this.unitsLabel = $('#map_units_label', this.container);
			
			this.portSelected = new signals.Signal();
			this.countrySelected = new signals.Signal();
			this.totalSelected = new signals.Signal();
		},
		createPlayerControls : function() {
			this.$super();	
		},
		
		createUSMap : function() {
			var ports = this.config.ports;
			var self = this;
			
			this.usMapContainer.vectorMap({
				map: 'us_terr_merc_en',
				regionsSelectable: false,
				markersSelectable: true,
				markersSelectableOne: true,
				markers: ports,
				markerStyle: {
						initial: {
							fill: '#FFFFFF',
							"fill-opacity": 0.1
						},
						selected: {
							fill: '#CA0020',
							"fill-opacity": 1
						},
						hover: {
							stroke : '#FFFF00',
							"stroke-width" : 3,
							"stroke-opacity" : 1
						}
					},
				backgroundColor: '#FFF',
				regionStyle: {
					initial: {
						fill: '#aaaaaa'
					},
					selected: {
						fill: '#CA0020'
					}
				},
				onRegionClick: function (event, index) {

				},
				onMarkerClick: function(event, index){
					var selectedMarkerUpdate = {};
					var code = [0, self.map_us.markers[index].config.id];
					$.each(self.map_us.getSelectedMarkers(), function(index, value) {
						selectedMarkerUpdate[value] = false;
					});
					$('#reset_button').addClass('red');
					for (var i = self.config.ports.length - 1; i >= 0; i--) {
						if (self.config.ports[i].id == code[1]){
							code[0] = i;
						}
					};
					if(self.selectedPort != self.config.ports[code[0]]['id']){
						selectedMarkerUpdate[index] = true;
						self.map_us.setSelectedMarkers(selectedMarkerUpdate);
						self.selectedPort = self.config.ports[code[0]]['id'];
						self.portSelected.dispatch(self.config.ports[code[0]]['id']);
					}
					return false;
				},
				onMarkerLabelShow: function(event, label, code){
					if (code) {
						if(self.data.PORT_SELECTED && self.data.PORTS[0] == self.portsCon[code].id){
							var val = self.map.getCurrentCounts()['TOT'];
							var units= null;
							if(self.title.split(" ")[1].toLowerCase() == "price"){
								units = "($/short tons)";
							} else if(self.title.split(" ")[1].toLowerCase() == "quantity"){
								units = "(short tons)";
							} else {
								units = "";
							}
							if(val !== void 0)
								$(label).html(
									$(label).html() + '<br />' + self.dateLabel + '<br />' +
									Highcharts.numberFormat(val, self.precision) + ' ' +  units
								);
						} else if (self.data.COUNTRY_SELECTED) {
							var val = self.map.getCurrentCounts()[self.portsCon[code].id];
							var units = null;
							if(self.title.split(" ")[1].toLowerCase() == "price"){
								units = "($/short tons)";
							} else if(self.title.split(" ")[1].toLowerCase() == "quantity"){
								units = "(short tons)";
							} else {
								units = "";
							}
							if(val !== void 0)
								$(label).html(
									$(label).html() + '<br />' + self.dateLabel + '<br />' +
									Highcharts.numberFormat(val, self.precision) + ' ' +  units
								);
						}
					} else {
						label.empty();
					}
				}
			});
			this.map_us = $('#map_us').vectorMap('get', 'mapObject');
		},

		destroy : function(){
			if(this.map && typeof this.map.destroy == 'function') this.map.destroy();
			if(this.map_us && typeof this.map_us.destroy == 'function') this.map_us.destroy();
		},

		updateUSMarkers : function(){
			var selectedMarkerUpdate = {};
			var updatePort = this.data.PORTS;
			var allPorts = this.map_us.markers;
			var self = this;
			if(this.data.PORT_SELECTED && !this.data.COUNTRY_SELECTED) {
				$.each(allPorts, function(index,value){
					for (var i = 0; i < updatePort.length; i++) {
						if (updatePort[i] == value.config.id) {
							selectedMarkerUpdate[index] = true;
							break;
						} else {
							selectedMarkerUpdate[index] = false;
						}
					}
				});
				
				this.map_us.setSelectedMarkers(selectedMarkerUpdate);
			}
			else {
			}
		},
		
		update : function(data, mapType, start, end, frequency, options){
			
			var self = this;
			var newIteration = 0;
			
			this.displayColumns = [];
			this.counts = [];
			
			this.title = data.TITLE;
			this.data = data;
			this.mapType = mapType;
			this.start = start;
			this.end = end;
			this.options = options;
			
			if(this.map){
				this.map.stop();
				this.map.destroy();
			}
			
			this.setButtonState(false);
			
			this.updateUSMarkers();
			self.title = data.TITLE;
			this.map = new ContinuousMap(this.container, 'world_mill_en');
			self.map = this.map;
			self.selectableRegions = [];
			for (var i = self.config.countries.length - 1; i >= 0; i--) {
				self.selectableRegions.push(self.config.countries[i].id);
			};

			this.units = data.UNITS || '';
			this.precision = data.PRECISION;

			this.unitsLabel.text(this.units);
			
			$.each(data.DATACOLUMNS, function(i, col){
				if(col >= start && col <= end){
					self.displayColumns.push(col);
					self.counts.push(data.DATA[col]);
				}
			});
			if(this.position !== void 0){
				newIteration = $.inArray(this.position, this.displayColumns);
				if(!~newIteration)
					newIteration = this.displayColumns.length - 1;
			}
			else{
				newIteration = this.displayColumns.length - 1;
			}
			this.map.animation.enabled = true;
			this.map.update(
				this.counts,
				this.title + ' ' + this.displayColumns[newIteration],
				$.extend(
				{
					animation:{
						enabled:true,
						callback : function(iteration){
							if(this.colors) {
								this.worldMap.series.regions[0].setValues(this.getCurrentColors());
							}
							else if(this.worldMap.series.regions && this.worldMap.series.regions.length > 0) {
								this.worldMap.series.regions[0].setValues(this.getCurrentCounts());
							}
							var sliderLabelCSS = {};
							var col = self.displayColumns[iteration];
							self.position = col;
							var dateLabel = (typeof col === 'undefined' || col == null) ? '' : DateCodeUtil.formatDateCode(col, frequency);
							self.dateLabel = dateLabel;
							self.map.setTitle(self.title + ' ' + dateLabel);
							self.sliderLabel.text(dateLabel);
							sliderLabelCSS.left = -0.5 * self.sliderLabel.width() + 0.5 * self.handle.width();
							if(self.sliderLabel.height() > 1.5 * parseInt(self.sliderLabel.css('font-size').slice(0, -2), 10))
								sliderLabelCSS.top = -0.5 * self.sliderLabel.height()
							else
								sliderLabelCSS.top = 0;
							self.sliderLabel.css(sliderLabelCSS);
							self.slider.slider('option', 'value', iteration);
							if(iteration == self.displayColumns.length - 1)
								self.setButtonState(false);
								
							// Update markers on us map for each iteration
								var allPorts = self.map_us.markers;
								var values = [];
								var currentColors = self.map.getCurrentColors();
								
								$.each(allPorts, function(index,value){
									var config = value.config;
									if(typeof currentColors != 'undefined' && typeof currentColors[config.id] != 'undefined') {
										config['style'] = {
											"fill" : currentColors[value.config.id],
											"fill-opacity" : 1
										};
									}
									else {
										config['style'] = {};
									}
									
									values.push(config);
								});
								
								var selectedMarkers = (self.data.PORT_SELECTED && !self.data.COUNTRY_SELECTED) ? self.map_us.getSelectedMarkers() : [];
								if(self.data.COUNTRY_SELECTED && !self.data.PORT_SELECTED){self.map.worldMap.setSelectedRegions(self.data.COUNTRIES)};
								self.map_us.removeAllMarkers();
								self.map_us.addMarkers(values);
								self.map_us.setSelectedMarkers(selectedMarkers);
						},
						iteration : newIteration
					},
					continuous : true,
					isGrowth : true,
					dataPrecision : 2,
					extremes : {
						min : -100,
						max : 100
					},
					legendItemFormatter : function(value){
						var ret;

						if(mapType == self.$class.GROWTH_MAP){
							ret = Highcharts.numberFormat(value, self.precision) + '%';
							if(value > 0)
								ret += '+'
						}
						else
							ret = Highcharts.numberFormat(value, self.precision);

						return ret;
					},
					'zoomOnScroll' : true,
					'onRegionLabelShow' : function(event, label, code){
						if($.inArray(code, self.selectableRegions) !== -1){
							if (code) {
								if(self.data.COUNTRY_SELECTED && self.data.COUNTRIES[0] == code){
									var val = self.map.getCurrentCounts()['TOT'];
									
									if(val !== void 0)
										//$(label).show();
										$(label).html(
											$(label).html() + '<br />' + self.dateLabel + '<br />' +
											Highcharts.numberFormat(val, self.precision) + ' ' +  self.units
										);
								} else if (self.data.PORT_SELECTED){
									var val = self.map.getCurrentCounts()[code];

									if(val !== void 0)
										//$(label).show();
										$(label).html(
											$(label).html() + '<br />' + self.dateLabel + '<br />' +
											Highcharts.numberFormat(val, self.precision) + ' ' +  self.units
										);
								}
							}
							else {
								return false;
							}
						} else {
							$("path[data-code='" + code + "'")
							.css({"cursor": "default", "stroke": "none"});
							return false;
						}
					},
					regionsSelectable: true,
					regionsSelectableOne: false,
					markersSelectable: false,
					markerStyle: {
					  initial: {
						fill: '#4DAC26'
					  },
					  selected: {
						fill: '#CA0020'
					  }
					},
					backgroundColor: '#FFFFFF',
					regionStyle: {
						initial: {
							fill: '#aaaaaa'
						},
						selected: {
							fill: '#CA0020',
							"fill-opacity": 1
						}
					},
					onRegionClick: function (event, index) {
						if($.inArray(index, self.selectableRegions) !== -1) {
							if(self.selectedPort != index){
								$('#reset_button').addClass('red');
								var selectedRegionUpdate = {};
								$.each(self.map.worldMap.getSelectedRegions(), function(index, value) {
									selectedRegionUpdate[value] = false;
								});
								selectedRegionUpdate[index] = true;
								self.map.worldMap.setSelectedRegions(selectedRegionUpdate);
								self.selectedPort = index;
								self.countrySelected.dispatch(index);
							}
						} 
						return false;
					}
				}, options)
			);
			this.map.reset(newIteration);
				
			this.slider.slider('option', 'min', 0);
			this.slider.slider('option', 'max', this.displayColumns.length - 1);
			this.slider.slider('option', 'value', newIteration);
			
			if(typeof this.sliderHandler == 'function') {
				this.slider.unbind('slide', this.sliderHandler);
			}
			this.slider.bind('slide', this.sliderHandler);
			
			if(typeof this.playHandler == 'function') {
				this.playButton.unbind('click', this.playHandler);
			}
			this.playButton.bind('click', this.playHandler);
			
			if(typeof this.pauseHandler == 'function') {
				this.pauseButton.unbind('click', this.pauseHandler);
			}
			this.pauseButton.bind('click', this.pauseHandler);
			
			if(typeof this.resetHandler == 'function') {
				this.resetButton.unbind('click', this.resetHandler);
			}
			this.resetButton.bind('click', this.resetHandler);
			this.resetButton.bind('click', function(){
					self.totalSelected.dispatch();
			});
		},
		
		setButtonState : function(playing){
			if(playing){
				this.playButton.attr('checked', false);
				$('label[for="' + this.playButton.attr('id') + '"]').hide();
				$('label[for="' + this.pauseButton.attr('id') + '"]').show();
			}
			if(!playing){
				this.pauseButton.attr('checked', false);
				$('label[for="' + this.pauseButton.attr('id') + '"]').hide();
				$('label[for="' + this.playButton.attr('id') + '"]').show();
			}
			this.playButton.button('refresh');
			this.pauseButton.button('refresh');
		},
		
		outputToArrOfArrs : function(){
			var ret = [],
			keys = [],
			regs,
			key, i, j,
			name,
			prefix,
			regData;
			var prefix = "";
			if(this.data.PORT_SELECTED == this.data.COUNTRY_SELECTED){
				var titleArr = this.title.split(" ");
			}
			if(this.data.PORT_SELECTED == true){
				if(titleArr){
					prefix = titleArr[0] + " " + titleArr[1];
					if(titleArr[0].toLowerCase() == "import"){
						prefix += " to total U.S. from "
					} else {
						prefix += " from total U.S. to "
					}
				}
				regs = this.data.COUNTRIES;
				regs.sort();
				for(key in regs){
					if(regs.hasOwnProperty(key) && regs[key] != "TOT"){
						for(var z in this.config.countries){
							if(regs[key] == this.config.countries[z].id){
								name = prefix + this.config.countries[z].name;
								break;
							} else {
								name =  prefix + regs[key];
							}
						}
						keys.push([
							regs[key],
							name
						]);
					}
				}
			}
			if(this.data.COUNTRY_SELECTED == true) {
				if(titleArr){
					prefix = titleArr[0] + " " + titleArr[1];
					if(titleArr[0].toLowerCase() == "import"){
						prefix += " from total world to "
					} else {
						prefix += " to total world from "
					}
				}
			 	regs = this.data.PORTS;
				regs.sort();
				for(key in regs){
					name = "";
					if(regs.hasOwnProperty(key) && regs[key] != "TOT"){
						for(var z in this.config.ports){
							if(regs[key] == this.config.ports[z].id){
								name = prefix + this.config.ports[z].name;
								break;
							} else {
								name = prefix + regs[key];
							}
						}
						keys.push([
							regs[key],
							name
						]);
					}
				}
			}
			ret.push(['Units: ' + this.data.UNITS]);
			var dispCols = this.displayColumns.slice();
			dispCols.unshift('');
			ret.push(dispCols);
			for(j=0; j < keys.length; j++){
				key = keys[j]
				ret.push(regData = ['"' + key[1] + '"']);
				for(i=0; i < this.counts.length; i++){
					regData.push(this.map.counts[i][key[0]] || '');
				}
			}	
			return ret;
		},
		
		__classvars__ : {
			CONTINUOUS_MAP : '0',
			GROWTH_MAP : '1'
		}
	});
});

define('text!templates/importExportSelection.html',[],function () { return '<div id="import_export_button" class="ui-buttonset">\r\n\t<input type="radio" id="import-radio" name="import_export" value="import"/>\r\n\t<label for="import-radio" alt="Import" title="Import" class="ui-button">Import</label>\r\n\t<input type="radio" id="export-radio" name="import_export" value="export"/>\r\n\t<label for="export-radio" alt="Export" title="Export" class="ui-button">Export</label>\r\n</div>\r\n<div id="quantity_price_button" class="ui-buttonset">\r\n\t<input type="radio" id="quantity-radio" name="quantity_price" value="quantity"/>\r\n\t<label for="quantity-radio" alt="Quantity" title="Quantity" class="ui-button">Quantity</label>\r\n\t<input type="radio" id="price-radio" name="quantity_price" value="price"/>\r\n\t<label for="price-radio" alt="Price" title="Price" class="ui-button">Price</label>\r\n</div>';});

define('js/view/VC_ImportExportSelection',[
	'classy',
	'signals',
	'text!templates/importExportSelection.html',
],
function(Class, signals, importExportSelectorTemplate)
{
	return Class.$extend({
		
		importExportSelect : null,
		quanitityPriceSelect : null,
		selectionChanged : null,
		
		__init__ : function(container){
			this.container = container;
			
			this.selectionChanged = new signals.Signal();
		},
		
		create : function(topicId, query){
			var self = this;
			this.container.html(
				Mustache.render(importExportSelectorTemplate, {})
			);
			this.importExportSelect = $('input[name="import_export"]', this.container)
			.change(function(){
				if(self.importExportSelect.filter(':checked').val() != '' || self.quantityPriceSelect.filter(':checked').val() != ''){
					var newTopicId = 39;
					if(self.importExportSelect.filter(':checked').val() === 'export'){
						newTopicId += 2;
					}
					if(self.quantityPriceSelect.filter(':checked').val() === 'price'){
						newTopicId += 1;
					}
					self.selectionChanged.dispatch(newTopicId);
				}

			});
			$('#import_export_button').buttonset();
			
			this.quantityPriceSelect = $('input[name="quantity_price"]', this.container)
			.change(function(){
				if(self.importExportSelect.filter(':checked').val() != '' || self.quantityPriceSelect.filter(':checked').val() != ''){
					var newTopicId = 39;
					if(self.importExportSelect.filter(':checked').val() === 'export'){
						newTopicId += 2;
					}
					if(self.quantityPriceSelect.filter(':checked').val() === 'price'){
						newTopicId += 1;
					}
					self.selectionChanged.dispatch(newTopicId);
				}
			});
			$('#quantity_price_button').buttonset();
			this.updateSelection(topicId, query);
		},
		
		updateSelection : function(topicId, query){
			if(topicId == 39 || topicId == 40){
				this.importExportSelect.filter('[value="import"]').attr("checked", true);
			} else {
				this.importExportSelect.filter('[value="export"]').attr("checked", true);
			}
			if(topicId == 39 || topicId == 41){
				this.quantityPriceSelect.filter('[value="quantity"]').attr("checked", true);
			} else {
				this.quantityPriceSelect.filter('[value="price"]').attr("checked", true);
			}
			$('#import_export_button').buttonset('refresh');
			$('#quantity_price_button').buttonset('refresh');
		}
	});
});
define('js/pages/ImportExportPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	//Facade
	'js/Facade',
	'js/Controller',
	//Model
	'js/model/ImportExportConfigProxy',
	'js/model/AggregateDataRequestProxy',
	'js/model/AggregateDataParameterProxy',
	'js/model/ImportExportAggregateDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/ImportExportMapDataProxy',
	'js/model/FootnoteProxy',
	//View
	'js/view/MediatorCoalTopicSelection',
	'js/view/MediatorImportExportForm',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorPinKeyVisualization',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorDualSliderTimeSelection',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorImportExportMap',
	'js/view/MediatorAnalysis',
	'js/view/MediatorImportExportDataGrid',
	'js/view/MediatorImportExportSelection',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'js/view/VC_Analysis',
	'js/view/VC_LineChart',
	'js/view/VC_ColumnChart',
	'js/view/VC_ImportExportMap',
	'js/view/VC_ImportExportSelection',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'datagrid',
	'qTipsHelp',
	'viewStack',
	// Templates
	'text!templates/tableTitle.html',
	'text!templates/footnotes.html',
	// JS and CSS
	'simpleModal'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	MapDataProxy,
	FootnoteProxy,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	VisualizationMediator,
	PinKeyVisualizationMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	TimeSelectionMediator,
	HelpTooltipMediator,
	AnimatedMapMediator,
	AnalysisMediator,
	DataGridMediator,
	ImportExportSelectionMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	TimeSelectionDialog,
	AnalysisDialog,
	TopicLineChart,
	TopicColumnChart,
	EnerdatAnimatedMap,
	ImportExportSelection,
	dateCodeUtils,
	GridFormatters,
	DataGrid,
	HelpTooltipDialog,
	ViewStack,
	tableTitleTemplate,
	footnoteTemplate
){
	var facade = null;
	
	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			$('#pagecontent').addClass('aggregate');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = AggregateDataFacade.getInstance(this.name);
				facade.sendNotification(AggregateDataFacade.STARTUP, request);
			}
			else
				facade.sendNotification(AggregateDataFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(AggregateDataFacade.UNLOAD);
			AggregateDataFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('aggregate');
		}
	})('js/pages/ImportExportPage');
	
	////////////
	// Facade //
	////////////
	
	var AggregateDataFacade = Facade.$extend({
		
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, TableParamsUpdatedCommand);
			this.registerCommand(this.$class.CHART_PARAMS_UPDATED, Controller.UpdateChartDataCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, TableSelectionUpdatedCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_DATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.TABLEDATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.DEFAULT_CHART_SET, Controller.UpdateChartedCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.MAP_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.RSE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.AGGREGATE_DATA_CLICK_THROUGH, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.SOURCE_KEY_SELECTED, Controller.OpenAPIWindowCommand);
			this.registerCommand(this.$class.PIN_KEY_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		
		__classvars__ : {
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new AggregateDataFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableParameter = {
				id : 'csv_table',
				type : 'csv',
				'class' : 'ico_csv',
				target : 'table',
				label : 'Table (CSV)'
			};
			var configProxy = new ConfigProxy();
			configProxy.setVisualizations(VisualizationDialog.defaultConfig.items.slice());
			configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
			configProxy.setDownloadParameters(
				{
					'map' : [
						{
							label : 'Map',
							children : [
								{
									id : 'map_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'map',
									label : 'Map (PNG)'
								},
                                {
                                    id : 'map_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'map',
                                    label : 'Map (PDF)'
                                },
                                {
                                    id : 'map_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'map',
                                    label : 'Map (SVG)'
                                },
                                {
                                    id : 'map_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'map',
                                    label : 'Map Embed'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'map_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'map',
									label : 'Map (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'columnchart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'columnchart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'columnchart',
									label : 'PDF'
								},
								{
									id : 'columnchart_png',
									type : 'png',
									'class': 'ico_img',
									target : 'columnchart',
									label : 'PNG'
								},
                                {
                                    id : 'columnchart_svg',
                                    type : 'svg',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'columnchart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'columnchart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'columnchart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'columnchart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'linechart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'linechart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'linechart',
									label : 'PDF'
								},
								{
									id : 'linechart_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'linechart',
									label : 'PNG'
								},
                                {
                                    id : 'linechart_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'linechart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'linechart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'linechart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'linechart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					]
				}
			);
			
			// Force initial load to use default values for coal rank, all coal ranks
			var body = note.getBody();
			body['query']['rank'] = '';
			
			var requestProxy = new RequestProxy(body);
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var mapDataProxy = new MapDataProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(mapDataProxy);
			this.facade.registerProxy(footnoteProxy);
			
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TableParamsUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ShowLoadMaskCommand);
			this.addSubCommand(Controller.UpdateTableDataProxyCommand);
		}
	});
	
	var TableSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(ResetChartedCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			//this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(ResetChartedCommand);
			//this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	var ChartDataUpdatedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
			
			if(parameterProxy.getChartType() == 'map'){
				mapDataProxy.updateMapData();
			}
		}
	});

	var ResetChartedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var body = note.getBody();
			if(typeof body != 'undefined' && typeof body.topic != 'undefined' && (body.topic < 39 || body.topic > 42)) {
				parameterProxy.resetCharted();
			}
			else {
				var visualizations = this.facade.retrieveProxy(ConfigProxy.NAME).getVisualizations();
				$.each(visualizations, function(index, value) {
					if(value['class'] != "map"){
						parameterProxy.deleteNonPinnedIds(value['class']);
					}
				});
			}
		}
	});
	
	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this; 
			
			var pageLayout = this.getViewComponent();

			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			//var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), {items : configProxy.getVisualizations()});
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			var dataGrid = new DataGrid(pageLayout.getTableContainer());
			var viewStack = new ViewStack(pageLayout.getChartContainer());
			var importExportSelection = new ImportExportSelection(pageLayout.getImportExportSelectionContainer());
			var pinKeyVisualizationDialog = new VisualizationDialog(pageLayout.getPinKeyVisualizationContainer(), {items : configProxy.getLinkTypes()});
			
			var mapContainer = viewStack.addView('map');
			var lineChartContainer = viewStack.addView('linechart');
			var columnChartContainer = viewStack.addView('columnchart');
			
			var columnChart = new TopicColumnChart(columnChartContainer);
			var lineChart = new TopicLineChart(lineChartContainer);
			var helpTooltipDialog = new HelpTooltipDialog();
			var animatedMap = new EnerdatAnimatedMap(mapContainer, {ports : configProxy.getPorts(), countries : configProxy.getCountries()});
			
			//var formMediator = new FormMediator(formDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var topTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var viewStackMediator = new ViewStackMediator(viewStack);
			var lineChartMediator = new LineChartMediator(lineChart);
			var columnChartMediator = new ColumnChartMediator(columnChart);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var animatedMapMediator = new AnimatedMapMediator(animatedMap);
			var pinKeyVisualizationMediator = new PinKeyVisualizationMediator(pinKeyVisualizationDialog);
			var importExportSelectionMediator = new ImportExportSelectionMediator(importExportSelection);
			
			this.facade.registerMediator(helpTooltipMediator);
			//this.facade.registerMediator(formMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(animatedMapMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);
			this.facade.registerMediator(importExportSelectionMediator);
			
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);

			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : false,
				showTransportationMode : false,
				showPrimeMovers : false,
				showPlantFuelTypes : false,
				showMineType : false,
				showMineStatus : false
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}))
				.on('change', '#rse_checkbox', function(e){
					self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
						'rse' : $(e.target).is(':checked') ? '1' : '0'
					});
				});
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});

		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				AggregateDataFacade.TABLEDATA_UPDATED,
				AggregateDataFacade.RSE_PARAM_UPDATED,
				AggregateDataFacade.TABLE_RENDERED,
				AggregateDataFacade.TOPIC_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();

			switch(note.getName()){
				case AggregateDataFacade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					
					var pageTitle = tableDataProxy.getTableTitle();
					if(parameterProxy.getReportIndex() != null) {
						pageTitle = configProxy.getReports()[parameterProxy.getReportIndex()].description;
					}
					
					window.document.title = 'Coal Data Browser - ' + pageTitle;
					
					/*this.getViewComponent().getTitleContainer().empty();
					this.getViewComponent().getTitleContainer().css('display', 'block');
					this.getViewComponent().getTitleContainer().append(Mustache.to_html(tableTitleTemplate, {title:pageTitle}));*/
					break;
				case AggregateDataFacade.TABLE_RENDERED:
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				case AggregateDataFacade.RSE_PARAM_UPDATED:
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					vc.getFootnoteContainer()
						.find('#rse_checkbox')
						.attr('checked', parameterProxy.getShowRSE()); 
					break;
				case AggregateDataFacade.TOPIC_PARAMS_UPDATED:
					var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
					var self = this;
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, {
							urlArgs : footnoteProxy.getUrlArgs(), 
							footnoteText : footnoteProxy.getFootnotes()
						}))
						.on('change', '#rse_checkbox', function(e){
							self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
								'rse' : $(e.target).is(':checked') ? '1' : '0'
							});
						});
						
					$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});
	

	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				/*'mainFilterButtonOpts' : {
					'selected' : false,
					'id' : 'main_filter_button'
				},*/
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainVisualizationButtonOpts' : {
					'selected' : false,
					'id' : 'main_visualization_button'
				},
				'overlayVisualizationButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_visualization_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
		}
	});
	
	var TopicLineChart = TopicLineChart.$extend({
		__classvars__ : $.extend(true, {}, TopicLineChart.prototype.__classvars__,  {
			defaultChartOptions : {
				credits : {
					text : Highcharts.eiaTheme.credits.text + '; Data source: U.S. Census Bureau'
				}
			}
		})
	});
	
	var TopicColumnChart = TopicColumnChart.$extend({
		__classvars__ : $.extend(true, {}, TopicColumnChart.prototype.__classvars__,  {
			defaultChartOptions : {
				credits : {
					text : Highcharts.eiaTheme.credits.text + '; Data source: U.S. Census Bureau'
				}
			}
		})
	});
	
	return page;
});
define('js/model/ListConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/ConfigMixinDownload'
],function(
	ConfigProxy,
	ConfigMixinDownload
){
	return ConfigProxy.$extend({
		__include__ : [
			ConfigMixinDownload
		]
	});
});
define('js/model/ParameterMixinDateCode',[], function(){
	return {
		getDatecode : function(){
			return this.getQuery()['datecode'];
		},
		
		validateDatecode : function(def, topic){
			var startCode, endCode;
			
			switch(def['freq']){
				case 'A':
					startCode = topic['annualStart'];
					endCode = topic['annualEnd'];
					break;
				case 'Q':
					startCode = topic['quarterlyStart'];
					endCode = topic['quarterlyEnd'];
					break;
				case 'M':
					startCode = topic['monthlyStart'];
					endCode = topic['monthlyEnd'];
					break;
			}
			
			this.checkVal(def, 'datecode', endCode, function(val){
				return val >= startCode && val <= endCode;
			});
			def['datecode'] = parseInt(def['datecode']);
		},
		
		resetDatecode : function(){
			delete this.getQuery()['datecode'];
		}
	};
});
define('js/model/ParameterMixinDateCodes',[
	'js/view/DateCodeUtil'
], function(
	DateCodeUtil
){
	return {
		getDateCodes : function(){
			return this.getData()['dateCodes'];
		},
		
		updateDateCodes : function(freq, topic){
			var dateCodes = [],
			startCode, endCode, i;
			
			switch(freq){
				case 'A':
					startCode = topic['annualStart'];
					endCode = topic['annualEnd'];
					break;
				case 'Q':
					startCode = topic['quarterlyStart'];
					endCode = topic['quarterlyEnd'];
					break;
				case 'M':
					startCode = topic['monthlyStart'];
					endCode = topic['monthlyEnd'];
					break;
			}
			
			for(i=startCode; i<=endCode; i=parseInt(DateCodeUtil.incrementDateCode(i, freq), 10))
				dateCodes.push(i);
				
			$.extend(this.getData(), {
				'dateCodes' : dateCodes
			})
		}
	};
});
	
define('js/model/ListParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/ParameterMixinTopic',
	'js/model/ParameterMixinReport',
	'js/model/ParameterMixinFrequency',
	'js/model/ParameterMixinAggColumn',
	'js/model/ParameterMixinDateCode',
	'js/model/ParameterMixinDateCodes',
	'js/model/ParameterMixinGetCharted',
	'js/model/ParameterMixinStateBasinType'
],function(
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	ParameterMixinTopic,
	ParameterMixinReport,
	ParameterMixinFrequency,
	ParameterMixinAggColumn,
	ParameterMixinDateCode,
	ParameterMixinDateCodes,
	ParameterMixinGetCharted,
	ParameterMixinStateBasinType
){
	return ParameterProxy.$extend({
		
		__include__ : [
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinAggColumn,
			ParameterMixinDateCode,
			ParameterMixinDateCodes,
			ParameterMixinGetCharted,
			ParameterMixinStateBasinType
		],
		
		getQueryKeys : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			topic = configProxy.getTopic(this.getTopicId()),
			ret = ['agg'],
			i;
			
			for(i=0; i<topic.aggColumns.length; i++)
				ret.push(topic.aggColumns[i]['filterGroup']['varName']);
				
			return ret.concat([
				'freq',
				'datecode',
				'rtype'
			]);
		},
		
		getGAString : function(params, topicId) {
			var qry = $.extend({}, this.getQuery(), params);
			var ret = 'topic/' + (topicId ? topicId : this.getTopicId()) + '/?' + (qry['agg'] ? 'agg=' + qry['agg'] : '') + (qry['freq'] ? '&freq=' + qry['freq'] : '') + (qry['datecode'] ? '&datecode=' + qry['datecode'] : '');
			
			$.each(this.getAggColumnParams(), function(key, value) {
				ret += '&' + key + '=' + qry[key];
			});
			
			return ret;
		},
		
		getGAStringByTopic : function(params, topicId) {
			params['datecode'] = null;
			return this.getGAString(params, topicId);
		},
		
		getFullPath : function(params, topicId){
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			return requestProxy.getPath() + '/' +
			(topicId !== void 0 ? topicId : this.getTopicId()) +
			this.getQueryString(params);
		},
		
		getFormParams : function(){
			return $.extend({
				'topic' : this.getTopicId(),
				'agg' : this.getAgg()
			}, this.getAggColumnParams())
		},
		
		getTableParams : function(){
			return $.extend({
				'topic' : this.getTopicId(),
				'freq' : this.getFreq(),
				'agg' : this.getAgg(),
				'datecode' : this.getDatecode(),
				'rtype' : this.getRegionType()
			}, this.getAggColumnParams());
		},
		
		getTableParamsString : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId()),
			ret = 'topic=' + this.getTopicId() +
				'&agg=' + this.getAgg(),
			aggColumn;
				
			for(i=0; i<topic.aggColumns.length; i++){
				aggColumn = topic['aggColumns'][i]['filterGroup']['varName'];
				ret += '&' + aggColumn + '=' + this.getQuery()[aggColumn];
			}
			
			ret += '&freq=' + this.getFreq();
			ret += '&datecode=' + this.getDatecode();
			ret += '&rtype=' + this.getRegionType();
			
			return ret;
		},

		getTimeParams : function(){
			return {
				'datecode' : this.getDatecode(),
				'freq' : this.getFreq()
			}
		},
		
		update : function(){
			var tmp = {},
			requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topicId = requestProxy.getTopicId(),
			tmpTopic = configProxy.getTopic(topicId),
			tableParamsUpdated = topicId !== this.getTopicId(),
			rtypeParamUpdated = false;
			
			this.updateTopicId(topicId);
			
			// Now loop through the keys again
			this.updateQuery(requestProxy.getQuery(), function(tmpKey, newVal, oldVal){
				// Check and see if our time parameters have changed
				tableParamsUpdated |= tmpKey == 'datecode';
				// Check and see if our frequency has changed
				tableParamsUpdated |= tmpKey == 'freq';
				// Check and see if our agg column order has changed
				tableParamsUpdated |= tmpKey == 'agg';
				// Check and see if the region type has been changed
				rtypeParamUpdated |= tmpKey == 'rtype';
				// Check and see if the parameter is a relavent agg column
				for(i=0; i < tmpTopic['aggColumns'].length && !tableParamsUpdated; i++){
					isAggColumn = tmpKey == tmpTopic['aggColumns'][i]['filterGroup']['varName'];
					tableParamsUpdated |= isAggColumn;
				}
			});
			
			this.updateDateCodes(this.getFreq(), tmpTopic);
			
			this.updateReportIndex();
			
			if(tableParamsUpdated)
				this.sendNotification(Facade.TABLE_PARAMS_UPDATED);
			if(rtypeParamUpdated)
				this.sendNotification(Facade.REGION_TYPE_PARAM_UPDATED);
		},
		
		validateQuery : function(vals){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId());
			
			this.validateFrequency(vals, topic);
			this.validateAggColumns(vals, topic);
			this.validateDatecode(vals, topic);
			this.validateRegionType(vals, topic);
		}
		
	});
});
define('js/model/ListRequestProxy',[
	'js/model/RequestProxy',
	'js/model/RequestMixinTopic'
],function(
	RequestProxy,
	RequestMixinTopic
){
	return RequestProxy.$extend({
		__include__ : [
			RequestMixinTopic
		]
	});
});
define('js/view/MediatorListMap',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ParameterProxy',
	'us-merc-en',
	'coal-basin-merc-en'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	ParameterProxy
) {
	return puremvc.Mediator.$extend({
	
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().regionClicked.add(function(stateCode, event){
				if(stateCode.match(/US-[A-Z][A-Z]/)) {
					self.sendNotification(Facade.STATE_MAP_CLICKED, {
						'stateCode' : stateCode.replace('US-', '')
					});
				}
			});
			
			this.getViewComponent().markerClicked.add(function(marker, event){
				var freq = self.facade.retrieveProxy(ParameterProxy.NAME).getTimeParams()['freq'];
				
				var body = {
					code : marker['code'],
					freq : freq
				};
				self.sendNotification(Facade.PLANT_LIST_CLICK_THROUGH, body, 'PlantMarkerClick');
			});
		},
		
		onRemove : function() {
			this.$super();
			this.getViewComponent().destroy();
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.MINE_HIGHLIGHT,
				Facade.REGION_TYPE_PARAM_UPDATED,				
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.UNLOAD
			]);
		},
		
		handleNotification : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var self = this;
			var vc = this.getViewComponent();
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
				case Facade.REGION_TYPE_PARAM_UPDATED:
					if(tableDataProxy.hasData()) {
						if(!tableDataProxy.getData()['SINGLE_STATE'] || parameterProxy.getRegionType() == 'b') {
							vc.mapContainer.addClass('clickable-regions ').removeClass('clickable-markers');
							vc.update(
								(parameterProxy.getRegionType() == 'b' ? tableDataProxy.getBasinMineCounts() : tableDataProxy.getMineCounts()),
								tableDataProxy.getMapTitle(),
								{
									'zoomOnScroll' : true,
									'map' : parameterProxy.getRegionType() == 'b' ? 'coal_basin_merc_en' : 'us_merc_en',
									'onRegionLabelShow' : function(event, label, code){
										self.onRegionLabelShow(event, label, code);
									},
									'onRegionOver' : function(e, code) {
										if(code.split('-')[0] == 'US' && parameterProxy.getRegionType() == 'b') {
											return false;
										}
										return true;
									},
									'regionStyle' : {
										hover : {
											stroke : '#FFFF96',
											"fill-opacity" : 1,
											"stroke-width" : 3,
											"stroke-opacity" : 1
										}
									},
									'onMarkerLabelShow' : null,
									'markers' : null,
									'legendTitle' : self.getLegendTitle()
								}
							);
							if(parameterProxy.getRegionType() == 'b') {
								$('path[data-code^="US-"]', this.container).attr('fill','#666666');
							}
						}
						else
						{
							vc.mapContainer.addClass('clickable-markers').removeClass('clickable-regions ');
							require(['us' + tableDataProxy.getData()['STATE_KEY'].toLowerCase() + '-merc-en'], function() {
								vc.update(
									tableDataProxy.getMineCounts(),
									tableDataProxy.getMapTitle(),
									{
										'zoomOnScroll' : true,
										'zoomMax' : 110,
										'map' : 'us' + tableDataProxy.getData()['STATE_KEY'].toLowerCase() + '_merc_en',
										'onRegionLabelShow' : null,
										'onMarkerLabelShow' : function(event, label, code) {
											return self.markerLabelShow(event, label, code);
										},
										'markers' : self.getMarkers(),
										'regionStyle' : { 
											initial : { 
												cursor: 'default'
											} 
										},
										'markerStyle' : {
											initial : { 
												cursor: 'pointer',
												fill: '#808080'
											}
										},
										'onMarkerOver' : function(event, code) {
											return self.onMarkerOver(event, code);
										},
										'onMarkerOut' : function(event, code) {
											return self.onMarkerOut(event, code);
										},
										'legendTitle' : self.getLegendTitle()
									}
								);
							});
						}
					}
					break;
				case Facade.MINE_HIGHLIGHT:
				case Facade.POWER_PLANT_HIGHLIGHT:
					var body = note.getBody();
					var elem;
					if(typeof vc.worldMap != 'undefined'){
						$.each(vc.worldMap.markers, function(index, value) {
							if(value.config.code == body.code) {
								elem = value.element;
							}
						});
					}
					if(elem != null) {
						if(body.highlight) {
							elem.applyAttr('stroke', '#FFFF96');
							elem.applyAttr('stroke-width', elem.properties['stroke-width']*2);
							/*if(window.SVGAngle) {
								elem.node.parentNode.appendChild(elem.node);
							}
							else {
								elem.node.style.zIndex = 1;
							}*/
						}
						else {
							elem.applyAttr('stroke', elem.properties['stroke']);
							elem.applyAttr('stroke-width', elem.properties['stroke-width']);
							/*if(!window.SVGAngle) {
								elem.node.style.zIndex = "";
							}*/
						}
					}
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'map' && note.body.type == 'png'){
						this.getViewComponent().exportMap(note.body.type);
					}
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				default: 
					this.$super(note);
					break;
			}
		},
		
		markerLabelShow : function(event, label, code) {
			var vc = this.getViewComponent();
			var value = vc.options.markers[code];
			var labelTxt = value['name'] + '<BR>' +
				'MSHA id: ' + value['code'] + '<BR>' +
				'Coal rank: ' + value['rank'] + '<BR>' +
				'Mine type: ' + value['type'] + '<BR>' +
				'Mine status: ' + value['status'] + '<BR>' +
				'Location: ' + value['latLng'][0] + ', ' + value['latLng'][1];
			$(label).html(labelTxt);
			return labelTxt;
		},
		
		onMarkerOver : function(event, code) {
			var vc = this.getViewComponent();
			
			this.sendNotification(Facade.MINE_HIGHLIGHT, {
				index : code,
				code : vc.worldMap.markers[code].config.code,
				highlight : true
			}, 'PlantListMapMarkerOver');
			return false;
		},
		
		onMarkerOut : function(event, code) {
			var vc = this.getViewComponent();
			
			this.sendNotification(Facade.MINE_HIGHLIGHT, {
				index : code,
				code : vc.worldMap.markers[code].config.code,
				highlight : false
			}, 'PlantListMapMarkerOut');
			return false;
		},
		
		onRegionLabelShow : function(event, label, code){
			var vc = this.getViewComponent();
			var value = vc.getCurrentCounts()[code];
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			if ((parameterProxy.getRegionType() == 'b' && code.split('-')[0] == 'BS') || (parameterProxy.getRegionType() != 'b' && code.split('-')[0] == 'US')) {
				$(label).html($(label).html() + ' ' + (value !== void 0 ? value : 0) + ' mines');
			}
		},
		
		getLegendTitle : function() {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			return tableDataProxy.getMineMarkers().length + ' total mines';
		},
		
		getMarkers : function() {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var vc = this.getViewComponent();
			var ret = $.merge([], tableDataProxy.getMineMarkers());
			
			if(typeof parameterProxy.isMine != 'function' || parameterProxy.isMine()){
				$.each(ret, function(index, value){
					vc.$class.styleMarkerMine(value);
				});
			}
			else {
				$.each(ret, function(index, value){
					vc.$class.styleMarkerPlant(value);
				});
			}
			return ret;
		},
		
		__classvars__ : {
			NAME : 'ListMapMediator'
		}
	});
});
define('js/model/ListTableDataProxy',[
	'js/Facade',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/MediatorListMap'
],function(
	Facade,
	ParameterProxy,
	TableDataProxy,
	ListMapMediator
){
	return TableDataProxy.$extend({
		
		isUpdating : false,
		
		__init__ : function(){
			this.$super(this.$class.NAME);
			this.setData('SINGLE_STATE',false);
		},
		
		loadTableData : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableParamsString = parameterProxy.getTableParamsString(),
			self = this;
			
			this.isUpdating = true;
			
			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
				this.isUpdating = false;
				this.sendNotification(Facade.TABLEDATA_UPDATED);
			}
			else{
				$.ajax(
					encodeURI(
						this.$class.PROXY_URL + '?method=' +
						this.$class.PROXY_METHOD + '&' + tableParamsString
					),{
						dataType : 'json',
						error : function(){
							self.sendNotification(Facade.ERROR);
						},
						success :  function(tableData){
							var dataView = new Slick.Data.DataView();
							dataView.setFilter(self.listFilter);
							dataView.setItems(tableData['DATA']);
							
							self.setData(tableParamsString, $.extend(tableData, {
								'dataView' : dataView,
								'mappedCounts' : (function(){
									var newCounts = {};
									for(var key in tableData.STATE_COUNTS)
										if(tableData.STATE_COUNTS.hasOwnProperty(key)){
											var count = tableData.STATE_COUNTS[key];
											if(count !== 0)
												newCounts[key.toLowerCase()] = count;
										}
									return newCounts;
								}()),
								'basinMappedCounts' : (function(){
									var newCounts = {};
									for(var key in tableData.BASIN_COUNTS)
										if(tableData.BASIN_COUNTS.hasOwnProperty(key)){
											var count = tableData.BASIN_COUNTS[key];
											if(count !== 0)
												newCounts[key.toLowerCase()] = count;
										}
									return newCounts;
								}())
							}));
							self.isUpdating = false;
							self.sendNotification(Facade.TABLEDATA_UPDATED);
						}
					}
				);
			}
		},
		
		listFilter : function(item, args){
			var found = true, field, value, tokens, token;
			
			if(args && args.searchString != ""){
				tokens = args.searchString.toUpperCase().split(' ');
				for(i = 0; i<tokens.length && found; i++){
					token = tokens[i];
					if(token != ''){
						for(field in item){
							if(item.hasOwnProperty(field) && item[field] !== null && item[field] !== undefined){
								value = item[field].toString().toUpperCase();
								if(found = ~value.indexOf(token))
									break;
							}
						}
					}
				}
			}
			
			return found;
		},
		
		updateListFilter : function(searchString){
			this.getTableData().setFilterArgs({
				'searchString' : searchString
			});
			this.getTableData().refresh();
		},
		
		getDataColumns : function(){
			return this.getData()['DATA_COLUMNS'];
		},
		
		getTableData : function(){
			return this.getData()['dataView'];
		},
		
		getTableTitle : function(){
			return this.getData()['DESCRIPTION'];
		},
		
		getMapTitle : function(){
			return this.getData()['MAP_TITLE'];
		},
		
		getMineCounts : function(){
			return this.getData()['mappedCounts'];
		},
		getBasinMineCounts : function(){
			return this.getData()['basinMappedCounts'];
		},
		
		
		getMineMarkers : function() {
			var data = this.getData()['DATA'];
			var ret = [];
			var mineMarker = this.facade.retrieveMediator(ListMapMediator.NAME).getViewComponent().$class.MINE_MARKER;
			
			$.each(data, function(index, value) {
				var tmp = {
					'latLng' : [ value['lat'], value['lon']],
					'name' : value['Mine Name'],
					'code' : value['MSHA ID'],
					'rank' : value['Coal Rank'],
					'type' : value['Mine Type'],
					'status' : value['Q4 Mine Status'],
					'Total Production' : value['Total Production']
				};

				var fuelType = value['Aggregate Fuel Type'];

				if (mineMarker.hasOwnProperty(fuelType)) {
					tmp['style'] = {
						fill: mineMarker[value['Aggregate Fuel Type']].color
					};
				};

				ret.push(tmp);
			});
			
			return ret;
		},
		
		hasData : function() {
			if(typeof this.getTableData() != 'undefined') {
				return true;
			}
			return false;
		},
		
		__classvars__ : {
			PROXY_URL : 'data/index.php',
			PROXY_METHOD : 'getMineList'
		}
	});
});
define('js/model/HelpToolTipProxy',[
	'puremvc',
	'js/view/HelpTooltipsCDB'
], 
function(
	puremvc,
	HelpTooltips
){
	return puremvc.Proxy.$extend({
		
		__init__ : function(){
			this.$super(this.$class.NAME, HelpTooltips);
		},
		
		getHelpTooltips : function(){
			return (this.getData())();
		},
		
		__classvars__ : {
			NAME : 'HelpTooltipProxy'
		}
	});
});
define('js/view/MediatorSearch',[
	'puremvc',
	'js/Facade'
], function(
	puremvc,
	Facade
) {
	return puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;

			this.getViewComponent().inputChanged.add(function(string){
				self.sendNotification(Facade.SEARCH_STRING_UPDATED, string);
			});
		},
		
		__classvars__ : {
			NAME : 'SearchMediator'
		}
	});
});
define('js/view/MediatorSingleSliderTimeSelection',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			
			this.getViewComponent().timeSelectionChanged.add(function(datecode){
				var tableDataProxy = self.facade.retrieveProxy(TableDataProxy.NAME);
				self.sendNotification(Facade.TIME_SELECTION_UPDATED, {
					'datecode' : datecode
				}, 'SingleSliderTimeSelectionChanged');
			});
			
			this.getViewComponent().frequencySelectionChanged.add(function(freq){
				self.sendNotification(Facade.FREQUENCY_SELECTION_UPDATED, {
					'freq': freq
				}, 'FrequencyChanged');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(parameterProxy.getTopicId()),
			timeParams = parameterProxy.getTimeParams(),
			dateCodes;
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					dateCodes = parameterProxy.getDateCodes();
					this.getViewComponent().create(
						topic,
						timeParams['freq'],
						dateCodes,
						timeParams['datecode']
						
					);
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'TimeSelectionMediator'
		}
	});
});
define('js/view/MediatorMineListMap',[
	'puremvc',
	'js/Facade',
	'js/model/ParameterProxy',
	'js/view/MediatorListMap'
], function(
	puremvc,
	Facade,
	ParameterProxy,
	ListMapMediator
) {
	return  ListMapMediator.$extend({
		onRegister : function(){
			var self = this;
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			this.getViewComponent().regionClicked.add(function(stateCode, event){
				if(stateCode.match(/(US-[A-Z][A-Z])|(BS-[A-Z][A-Z][A-Z])/)) {
					if((parameterProxy.getRegionType() == 'b' && stateCode.split('-')[0] == 'BS') || (parameterProxy.getRegionType() != 'b' && stateCode.split('-')[0] == 'US')) {
						self.sendNotification(Facade.STATE_MAP_CLICKED, {
							'stateCode' : stateCode.split('-')[1]
						});
					}
				}
			});
			
			this.getViewComponent().markerClicked.add(function(marker, event){
				return self.markerClickHandler(marker, event);
			});
		},
		
		markerClickHandler : function(marker, event){
			var freq = this.facade.retrieveProxy(ParameterProxy.NAME).getTimeParams()['freq'];
			
			var body = {
				code : marker['code'],
				freq : freq
			};
			this.sendNotification(Facade.MINE_LIST_CLICK_THROUGH, body, 'MineMarkerClick');
		},
		markerLabelShow : function(event, label, code){
			var labelTxt = this.$super(event, label, code);
			var vc = this.getViewComponent();
			var value = vc.options.markers[code];
			labelTxt += '<BR>' + 'Total production: ' + Highcharts.numberFormat(value['Total Production'], self.precision);
			$(label).html(labelTxt);
			return labelTxt;
		}
	});
});
define('js/view/MediatorMineListDataGrid',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/view/GridFormatters',
	'js/model/TableDataProxy'
],function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	GridFormatters,
	TableDataProxy
){
	return puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},

		onRegister : function(){
			var self = this;
			this.getViewComponent().onClick.add(function(row, cell, datum){
				var vc = self.getViewComponent(), columns = vc.columns;
				var chartType = columns[cell].iconClass;
				var columnDef;
				if(cell === 0){
					var freq = self.facade.retrieveProxy(ParameterProxy.NAME).getTimeParams()['freq'];
					var label = 'mine/' + datum["MSHA ID"] + '?freq=' + freq;
					
					var body = {
						label : label,
						code : datum['MSHA ID'],
						freq : freq
					};

					self.sendNotification(Facade.MINE_LIST_CLICK_THROUGH, body, 'MineListClickThrough');
				}
			});

			this.getViewComponent().container.on('mouseover', '.slick-row', function(event) {
				self.sendNotification(Facade.MINE_HIGHLIGHT, {
					code : self.getViewComponent().slickgrid.getDataItem($(event.currentTarget).index())['id'],
					highlight : true
				}, 'MineListTableRowOver');
			});
			this.getViewComponent().container.on('mouseout', '.slick-row', function(event) {
				self.sendNotification(Facade.MINE_HIGHLIGHT, {
					code : self.getViewComponent().slickgrid.getDataItem($(event.currentTarget).index())['id'],
					highlight : false
				}, 'MineListTableRowOut');
			});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.MINE_HIGHLIGHT,
				Facade.UNLOAD
			]);
		},
		
		handleNotification : function(note){
			var body = note.getBody();
			var vc = this.getViewComponent();
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
					timeParams = parameterProxy.getTimeParams(),
					data = tableDataProxy.getTableData(),
					columns = this.generateColumns(
						tableDataProxy.getDataColumns(),
						data,
						timeParams['freq']
					);
					vc.update(data, columns, {});
					vc.slickgrid.setSortColumn(columns[0].id, true);
					this.sendNotification(Facade.TABLE_RENDERED);
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if(note.body.target == 'table' && note.body.type == 'csv'){
						var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
						var csvData = [
							['"' + tableDataProxy.getTableTitle() + '"'],
							['"' + window.location.href + '"'],
							['"' + Date() + '"'],
							['"Source: U.S. Energy Information Administration"']
						].concat(this.getViewComponent().outputToArrOfArrs());
						this.sendNotification(Facade.CSV_DATA_GENERATED, csvData);
					}
					this.sendNotification(Facade.CSV_DATA_GENERATED, csvData);
					break;
				case Facade.UNLOAD:
					vc.destroy();
					break;
				case Facade.MINE_HIGHLIGHT:
					var row = this.getViewComponent().slickgrid.getData().getRowById(body.code);
					
					if(body.highlight) {
						$('.slick-row:eq(' + row + ')', vc.container).addClass('highlight');
					}
					else {
						$('.slick-row:eq(' + row + ')', vc.container).removeClass('highlight');
					}
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		generateColumns : function(dataColumns, data, freq){
			var columns=[],
			i, columnKey;
			
			for(i = 0; i < dataColumns.length; i++){
				columnKey = dataColumns[i];
				columns.push({
					'id' : columnKey,
					'name' : columnKey,
					'field' : columnKey,
					'displayFormatter' : i == 0 ? this.$class.linkCellFormatter : void 0,
					'sortable' : true,
					'headerCssClass' : 'slick-header-column-sortable',
					'cssClass' : i==7 ? 'right-align' : '',
					'dataFormatter' : i == 7 ? GridFormatters.numericalDataFormatter : i == 6 ? GridFormatters.numericalDataFormatter : void 0,
					'freq' : freq 
				})
			}
			
			return columns;
		},
		
		__classvars__ : {
			
			NAME : 'DataGridMediator',
			
			linkCellFormatter : function(row, cell, value, columnDef, datum){
				return value !== '' ? '<span class="link_cell">' + value + '</span>' : value;
			}
		}
	});
});

define('text!templates/searchBox.html',[],function () { return '<div id="searchbox">\r\n\t<input type="text" name="searchInput" placeholder="{{searchPrompt}}"></input>\r\n\t<a id="search_box_reset">Reset<span> </span></a>\r\n</div>';});

define('js/view/VC_Search',[
	'classy',
	'signals',
	'text!templates/searchBox.html',
	'mustache'
],
function(Class, signals, searchBoxTemplate)
{
	return 	Class.$extend({
		
		input : null,
		resetButton : null,
		inputChanged : null,
		
		__init__ : function(container, options){
			var self = this;
			
			this.container = container;
			this.container.html(
				Mustache.render(
					searchBoxTemplate,
					options || this.$class.templateOpts
				)
			);
			
			this.inputChanged = new signals.Signal();
			this.input = this.container.find('input');
			this.resetButton = this.container.find('#search_box_reset');
			this.input.keyup(function(e){
				self.inputChanged.dispatch(self.input.val());
			});
			this.resetButton.click(function(e){
				self.input.val('');
				self.inputChanged.dispatch(self.input.val());
			});
		},
		
		__classvars__ : {
			templateOpts : {
				searchPrompt : 'search all fields'
			}
		}
		
	});
});
null;


/**
 *	The 'bucketMap' module creates a class that extends the map and has functions for generating the legend for the map by
 *  using buckets for the data values.
 *	
 *	@module bucketMap
 *	@requires  map, signals, jvectorMap, usa-en
 */

 define('bucketMap',
[
	'map',
	'signals',
	'order!jVectorMap',
	'order!usa-en'
],
function(Map, signals){
	
	return Map.$extend({
		/**
		 *	Constructor Function for bucketMap
		 *	@method __init__
		 *	@param {JQuery} container A JQuery object representing a <DIV> that will contain the bucketMap component.
		 */
		__init__ : function(container, basemap){
			var self = this;
			
			this.$super(container, basemap);
			
			$.extend(this.options, { 
				bucketFunction: this.$class.linearBucketFunction
			});
		},
		
		createLegend : function(){
			var self = this;
			var minVal, maxVal, prevMinVal, prevMaxVal, displayMin, displayMax;
			this.legendContainer.empty();
			var innerLegendContainer = $('<span style="display: inline-block; text-align:center;"></span>').appendTo(this.legendContainer);
			if (this.options.legendTitle){
				$('<span class="map_legend_title">' + this.options.legendTitle + '</span>').appendTo(innerLegendContainer);
				$('<br />').appendTo(innerLegendContainer);
			}
			$.each(this.buckets, function(index, bucket){
				var markerContainer = $('<span class="marker_container"></span>');
				$('<span class="map_legend_marker"></span>').appendTo(markerContainer)
				.css('background-color', self.$class.palette[index])
				.after('<span>' + (bucket.label ? bucket.label : self.$class.defaultLegendLabelFunction.call(self,bucket)) + '</span>'
				);
				markerContainer.appendTo(innerLegendContainer);
				return true;
			});
			var markerContainer = $('<span class="marker_container"></span>');
			$('<span class="map_legend_marker"></span>').appendTo(markerContainer).css('background-color', '#CCC').after('<span>Value is not available</span>');
			markerContainer.appendTo(innerLegendContainer);
		},

		assignColors : function() {
			
			this.buckets = this.options.bucketFunction.call(this, this.counts, this.$class.palette);
			
			var ret = [], state, count, bucket, ratio;
			for(var i = 0; i < this.counts.length; i++) {
				for (state in this.counts[i]) {
					count = parseFloat(Highcharts.numberFormat(this.counts[i][state], this.options.dataPrecision, '.', ''));
					for(var j = 0; j < this.buckets.length; j++) {
						if(this.buckets[j].testValue && typeof this.buckets[j].testValue == 'function') {
							if(this.buckets[j].testValue(count)) {
								if(!ret[i]) ret[i] = {};
								ret[i][state] = this.$class.palette[j];
							}
						}
						else {
							if(this.$class.defaultBucketTestFunction.call(this.buckets[j], count)) {
								if(!ret[i]) ret[i] = {};
								ret[i][state] = this.$class.palette[j];
							};
						}
					}
				}
			}
			
			this.colors = ret;
		},

		__classvars__ : {
			
			linearBucketFunction : function(counts, palette) {
				var ret = [], state;

				var step = (this.max - this.min) / palette.length;
				
				for(var i = 0; i < palette.length; i++) {
					var bucket = {min: null, max: null};
					bucket.min = parseFloat(Highcharts.numberFormat(this.min + step*i, this.options.dataPrecision, '.', ''));
					bucket.min = (i == 0 ? bucket.min : bucket.min + 1/Math.pow(10, this.options.dataPrecision));
					bucket.max = parseFloat(Highcharts.numberFormat(this.min + step*(i+1), this.options.dataPrecision, '.', ''));
					bucket.max = (bucket.max < bucket.min ? bucket.min : bucket.max);
					bucket.mid = (bucket.max - bucket.min) / 2 + bucket.min;
					
					if((ret.length == 0 || bucket.min != ret[ret.length-1].min || bucket.max != ret[ret.length-1].max) && (!(isNaN(bucket.min) || isNaN(bucket.max)))) {
						ret.push(bucket);
					}
					if(bucket.max >= this.max) {
						break;
					}
				}
				
				return ret;
			},
			
			/**
			* This is the default function used to test if a value falls into a bucket. If a bucket does not have a testValue function
			* on it, this function will be called instead in the assignColors function. It takes the value and returns a boolean
			* indicating whether the value belongs in the bucket.
			*
			* @method defaultBucketTestFunction
			* @param {Object} val The value to be tested for acceptance into the bucket.
			* @return {Boolean} Returns true if the value belongs in the bucket.
			*/
			defaultBucketTestFunction : function(val) {
				if(this.min && this.max) {
					return (val >= this.min && val <= this.max);
				}
				else if(this.min) {
					return (val >= this.min);
				}
				else if(this.max) {
					return (val <= this.max);
				}
				return false;
			},
			
			/**
			* This is the defaul function for generating the labels for the buckets in the legend. If a label property does not 
			* exist on a bucket this function is called to generate the label for the legend.
			*
			* @method defaultLegendLabelFunction
			* @param {Object} bucket An object with two properties, min and max.
			* @return {String} Returns the string to be used as a label for a bucket in the legend.
			*/
			defaultLegendLabelFunction : function(bucket) {
				var displayMin, displayMax;
				
				if(this.options.legendItemFormatter){
					displayMin = self.options.legendItemFormatter.call(self, bucket.min);
					displayMax = self.options.legendItemFormatter.call(self, bucket.max);
				}
				else{
					displayMin = bucket.min;
					displayMax = bucket.max;
				}
				
				return displayMin + (bucket.min < bucket.max ? ' to ' + displayMax : '');
			}
		}
	});
});

define('js/view/VC_MineListMap',[
	'bucketMap', 
	'us-merc-en'
],function(
	ListMap
){
	return ListMap.$extend({
		createLegend : function(){
			if (this.options.legendTitle){
				$('.legend_container h2', this.container).remove();
				$('.legend_container', this.container).prepend('<h2>' + this.options.legendTitle + '</h2>');
			}
			if(this.options.markers != null) {
				this.legendContainer.empty();
				var innerLegendContainer = $('<span class="plantMarkers" style="display: inline-block; text-align:center;"></span>').appendTo(this.legendContainer);
				
				
				$.each(this.$class.MINE_MARKER, function(key, value) {
					var markerContainer = $('<span class="marker_container" style="clear:both; width:100%"></span>');
					if(value.type == 'ring'){
						$('<span class="map_legend_ring"></span>').appendTo(markerContainer)
						.css('background-color', value.color)
						.after('<span>' + value.name + '</span>');
					} else {
					$('<span class="map_legend_marker"></span>').appendTo(markerContainer)
					.css({'background-color': value.color, 'clear': 'both'})
					.after('<span>' + value.name + '</span>');
					}
					markerContainer.appendTo(innerLegendContainer);
					return true;
				});
				$('#map_legend', this.container).append('<br />');
				$('#map_legend', this.container).append('<span .class="marker_disclaimer">Mine/plant sizes not to scale.</span>');
			}
			else {
				this.options.legendTitle = null;
				this.$super();
			}
		},
		
		createMarkerOps : function() {
			var markerValues = [], min = null, max = null;
			
			if(this.options.markers != null) {
				$.each(this.options.markers, function(index, value) {
					if(typeof value['latLng'] == 'object' && value['latLng'].length == 2 && value['latLng'].join('') != '') {
						markerValues.push(value['Total Production']);
						min = ((min == null) ? value['Total Production'] : Math.min(min, value['Total Production']));
						max = ((max == null) ? value['Total Production'] : Math.max(max, value['Total Production']));
					}
				});
			}
			return [{
				attribute: 'r',
				scale : [5, 15],
				values : markerValues,
				min : min,
				max : max
			}];
		},
		
		__classvars__ : {
			MINE_MARKER : {
				'ASUR' : {name: 'Active mine - surface', color: Highcharts.eia_dk_red, type:'fill'},
				'AUND' : {name: 'Active mine - underground' , color: Highcharts.eia_dk_blue, type:'fill'},
				'AOTH' : {name: 'Active mine - other', color: Highcharts.eia_tan, type:'fill'},
				'ISUR' : {name: 'Inactive mine - surface', color:Highcharts.eia_dk_red, type:'ring'},
				'IUND' : {name: 'Inactive mine - underground' , color: Highcharts.eia_dk_blue, type:'ring'},
				'IOTH' : {name: 'Inactive mine - other', color: Highcharts.eia_tan, type:'ring'},
				'PLNT' : {name: 'Plant', color: '#000000', type: 'fill'}
			},
			
			styleMarkerMine : function(marker){
				var style = {};
				switch(marker.type){
					case "Surface":
						style = {
							fill : this.MINE_MARKER['ASUR'].color,
							stroke : this.MINE_MARKER['ASUR'].color
						};
						break;
					case "Underground":
						style = {
							fill : this.MINE_MARKER['AUND'].color,
							stroke : this.MINE_MARKER['AUND'].color
						};
						break;
					default:
						style = {
							fill : this.MINE_MARKER['AOTH'].color,
							stroke : this.MINE_MARKER['AOTH'].color
						};
						break;
				}
				if(!(marker.status.toLowerCase() == "active" ||
					 marker.status.toLowerCase() == "active, men working, not producing" ||
					 marker.status.toLowerCase() == "active, men not working, not producing")){
						style['fill-opacity'] = 0;
						style['stroke-width'] = 2;
				} else {
					style['stroke'] = Highcharts.eia_grey;
				}
				marker.style = style;
			},
			
			styleMarkerPlant : function(marker) {
				marker.style = {
					fill : this.MINE_MARKER['PLNT'].color,
					stroke : Highcharts.eia_grey
				};
				
			}
		}
	});
});
define('js/view/VC_DataViewGrid',[
	'datagrid'
], function(
	DataGrid
){
	return DataGrid.$extend({
		
		onViewRowCountChanged : null,
		onViewRowsChanged : null,
		
		__init__ : function(container){
			var self = this;
			
			this.onViewRowCountChanged = function(e, args){
				self.slickgrid.updateRowCount();
				self.slickgrid.render();
			};
			
			this.onViewRowsChanged = function(e, args){
				self.slickgrid.invalidateRows(args.rows);
				self.slickgrid.render();
			};
			
			this.$super(container);
		},
		
		setData : function(data){
			var self = this;
			if(this.data && this.data.getLength /*is it a dataview?*/){
				this.data.onRowCountChanged.unsubscribe(this.onViewRowCountChanged);
				this.data.onRowsChanged.unsubscribe(this.onViewRowsChanged);
			}
			if(data.getLength /*is this also a dataview?*/){
				data.onRowCountChanged.subscribe(this.onViewRowCountChanged);
				data.onRowsChanged.subscribe(this.onViewRowsChanged);
			}
			this.$super(data);
		}
	})
});

define('text!templates/map.html',[],function () { return '<h2 id="mapTitle" class="map_title"></h2>\r\n<div id="mapContainer" class="map_container"></div>\r\n<div id="controls_container" class="legend_container">\r\n\t<div id="map_legend"></div>\r\n</div>\r\n';});

define('js/pages/MineListPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/ListConfigProxy',
	'js/model/ListParameterProxy',
	'js/model/ListRequestProxy',
	'js/model/ListTableDataProxy',
	'js/model/HelpToolTipProxy',
	'js/model/FootnoteProxy',
	'js/view/MediatorCoalTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorDownload',
	'js/view/MediatorSearch',
	'js/view/MediatorSingleSliderTimeSelection',
	'js/view/MediatorMineListMap',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorMineListDataGrid',
	'js/view/MediatorStateCoalBasinSelection',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Search',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'js/view/VC_MineListMap',
	'qTipsHelp',
	'js/view/VC_DataViewGrid',
	'js/view/VC_StateCoalBasinSelection',
	'js/view/DateCodeUtil',
	// Templates
	'text!templates/main.html',
	'text!templates/filter_button.html',
	'text!templates/download_button.html',
	'text!templates/tableTitle.html',
	'text!templates/map.html',
	'text!templates/footnotes.html',
	'slickdataview',
	'us-merc-en'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	ParameterProxy,
	RequestProxy,
	TableDataProxy,
	HelpTooltipProxy,
	FootnoteProxy,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	SearchMediator,
	TimeSelectionMediator,
	ListMapMediator,
	HelpTooltipMediator,
	DataGridMediator,
	StateCoalBasinSelectionMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	SearchBox,
	TopicSelectionDialog,
	TimeSelectionDialog,
	MineListMap,
	HelpTooltipDialog,
	DataViewGrid,
	StateCoalBasinSelection,
	dateCodeUtils,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplate,
	mapTemplate,
	footnoteTemplate
){
	var facade = null;
	
	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			$('#pagecontent').addClass('mineList');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = MineListFacade.getInstance(this.name);
				facade.sendNotification(MineListFacade.STARTUP, request);
			}
			else
				facade.sendNotification(MineListFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(MineListFacade.UNLOAD);
			MineListFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('mineList');
		}
	})('js/pages/MineListPage');
	
	////////////
	// Facade //
	////////////
	
	var MineListFacade = Facade.$extend({
		
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.SEARCH_STRING_UPDATED, UpdateMineListSearchCommand);
			this.registerCommand(this.$class.STATE_MAP_CLICKED, UpdateStateFilterCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.MINE_LIST_CLICK_THROUGH, Controller.UpdateMineHashCommand);
			this.registerCommand(this.$class.REGION_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		
		__classvars__ : {
			
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new MineListFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var requestProxy = new RequestProxy(note.getBody());
			var configProxy = new ConfigProxy();
			configProxy.setDownloadParameters(
				[
					{
						label : 'Data',
						children : [
							{
								id : 'csv_table',
								type : 'csv',
								'class' : 'ico_csv',
								target : 'table',
								label : 'Table (CSV)'
							}
						]
					}
				]
			);
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(footnoteProxy);
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	var FrequencySelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var UpdateMineListSearchCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			tableDataProxy.updateListFilter(note.getBody());
		}
	});
	
	var UpdateStateFilterCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			stateCode = note.getBody().stateCode, filterArray = [], filter = {geo:''}, binaryString = "";
			
			var topic = configProxy.getTopic(parameterProxy.getTopicId());
			
			for(var i = 0; i < topic.aggColumns.length; i++) {
				if(topic.aggColumns[i].filterGroup.varName == 'geo') {
					var filterValues = topic.aggColumns[i].filterGroup.filterValues;
					for(var j = 0; j < filterValues.length; j++) {
						if(filterValues[j].filterValue == stateCode) {
							filterArray[filterValues[j].valueId] = 1;
							break;
						}
					}
					break;
				}
			}
			
			for(i = 0; i < Math.ceil(filterArray.length / 5) * 5; i++) {
				if(filterArray[i] && filterArray[i] == 1) {
					binaryString += "1";
				}
				else {
					binaryString += "0";
				}
				if((i % 5) == 4) {
					filter['geo'] = filter['geo'] + parseInt(binaryString, 2).toString(32);
					binaryString = "";
				}
			}
			filter['geo'] = (filter['geo'] == "") ? "g" : filter['geo'];
			
			this.sendNotification(Facade.TABLE_SELECTION_UPDATED, filter, 'StateMapCicked');
		}
	});
	

	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var pageLayout = this.getViewComponent();
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			pageLayout.getPinKeyVisualizationContainer().css('display', 'none');
			
			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var searchBox = new SearchBox(pageLayout.getSearchContainer());
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var dataGrid = new DataViewGrid(pageLayout.getTableContainer());
			var helpTooltipDialog = new HelpTooltipDialog();
			var listMap = new MineListMap(pageLayout.getChartContainer(), 'us_merc_en');
						
						
			var formMediator = new FormMediator(formDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var searchMediator = new SearchMediator(searchBox);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var topTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var listMapMediator = new ListMapMediator(listMap);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			
			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(formMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(searchMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(listMapMediator);
			
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : false,
				showTransportationMode : false,
				showPrimeMovers : false,
				showPlantFuelTypes : false,
				showMineType : false,
				showMineStatus : false
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}));
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
			
			if(typeof parameterProxy.hasStateBasinFilterGroup == 'function'){
				if(parameterProxy.hasStateBasinFilterGroup(configProxy.getTopic(requestProxy.getTopicId()))) {
					var regionTypeSelection = new StateCoalBasinSelection(pageLayout.getRegionTypeSelectionContainer(), { rtype : parameterProxy.getRegionType() });
					var regionTypeSelectionMediator = new StateCoalBasinSelectionMediator(regionTypeSelection);
					this.facade.registerMediator(regionTypeSelectionMediator);
				}
			}
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.TABLE_RENDERED
			]);
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					var vc = this.getViewComponent();
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					
					var pageTitle = tableDataProxy.getTableTitle();
					
					this.getViewComponent().getTitleContainer().empty();
					this.getViewComponent().getTitleContainer().css('display', 'block');
					this.getViewComponent().getTitleContainer().append(Mustache.to_html(tableTitleTemplate, {title:pageTitle}));
					
					window.document.title = 'Coal Data Browser - ' + pageTitle;
					break;
				case Facade.TABLE_RENDERED:
					var vc = this.getViewComponent();
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});
	
	var ListMapMediator = ListMapMediator.$extend({
		getMarkers : function() {
			return this.$super().sort(function(a, b) {
				return b['Total Production'] - a['Total Production'];
			});
		}
	});
	
	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
			this.getChartContainer().html(Mustache.render(mapTemplate));
		}
	});
	
	return page;
});
define('js/model/ConfigMixinPreviousPage',[], function(){
	return {
		getPreviousPage : function() {
			return this.getData().previousPage;
		},
		setPreviousPage : function(value) {
			this.getData().previousPage = value;
		}
	};
});
define('js/model/SiteDataConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/ConfigMixinVisualization',
	'js/model/ConfigMixinDownload',
	'js/model/ConfigMixinViewStack',
	'js/model/ConfigMixinLinkType',
	'js/model/ConfigMixinPreviousPage'
], function(
	ConfigProxy,
	ConfigMixinVisualization,
	ConfigMixinDownload,
	ConfigMixinViewStack,
	ConfigMixinLinkType,
	ConfigMixinPreviousPage
){
	return ConfigProxy.$extend({
		__include__ : [
			ConfigMixinVisualization,
			ConfigMixinDownload,
			ConfigMixinViewStack,
			ConfigMixinLinkType,
			ConfigMixinPreviousPage
		]
	});
});
define('js/model/SiteDataRequestProxy',[
	'js/model/RequestProxy',
	'js/model/RequestMixinTopic'
], function(
	RequestProxy,
	RequestMixinTopic
){
	return RequestProxy.$extend({
		__include__ : [
			RequestMixinTopic
		],
		
		getId : function(){
			return this.getData()['1'];
		}
	});
});
define('js/model/ParameterMixinSite',[], function(){
	return {
		getId : function(){
			return this.getData()['id'];
		},
		
		updateId : function(id){
			$.extend(this.getData(), {
				'id' : id
			});
		}
	};
});
define('js/model/SiteDataParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/ParameterMixinSite',
	'js/model/ParameterMixinTopic',
	'js/model/ParameterMixinReport',
	'js/model/ParameterMixinFrequency',
	'js/model/ParameterMixinStartEnd',
	'js/model/ParameterMixinChart',
	'js/model/ParameterMixinLinkType'
], function(
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	ParameterMixinSite,
	ParameterMixinTopic,
	ParameterMixinReport,
	ParameterMixinFrequency,
	ParameterMixinStartEnd,
	ParameterMixinChart,
	ParameterMixinLinkType
){
	return ParameterProxy.$extend({
		
		__include__ : [
			ParameterMixinSite,
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinStartEnd,
			ParameterMixinChart,
			ParameterMixinLinkType
		],
		
		getQueryKeys : function(){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId()),
			ret = ['agg'],
			i;
			
			for(i=0; i<topic.aggColumns.length; i++)
				ret.push(topic.aggColumns[i]['filterGroup']['varName']);
				
			$.each(configProxy.getVisualizations(), function(index, value) {
				ret.push(value['class']);
			});
			
			return [
				'freq',
				'start',
				'end',
				'charted',
				'chartindexed',	
				'ctype',
				'ltype'
			];
		},
		
		
		getGAString : function(params, topicId) {
			var qry = $.extend({}, this.getTableParams(), params);
			var ret = 'id/' + qry['id']  + '/?' + (qry['freq'] ? '&freq=' + qry['freq'] : '');
			
			return ret;
		},
		
		getGAStringByTopic : function(params, topicId) {
			var qry = $.extend({}, this.getQuery(), params);
			var ret = 'topic/' + (topicId ? topicId : this.getTopicId()) + '/?' + (qry['agg'] ? 'agg=' + qry['agg'] : '') + (qry['freq'] ? '&freq=' + qry['freq'] : '');
			
			$.each(qry, function(key, value) {
				if(key != 'agg' && key != 'freq' && key != 'topicId') {
					ret += '&' + key + '=' + qry[key];
				}
			});
			
			return ret;
		},
		
		getTableParams : function(){
			return {
				'id' : this.getId(),
				'freq' : this.getFreq(),
				'ids' : this.getCharted('pin')
			};
		},
		
		getTableParamsString : function(){
			return 'id=' + this.getId() +
				'&freq=' + this.getFreq() +
				'&ids=' + this.getCharted('pin');
		},
		
		getChartParams : function(){
			var self = this;
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var params = {
				'chartindexed' : this.getChartIndexed(),
				'ctype' : this.getChartType(),
				'columnendpoints' : this.getColumnEndpoints(),
				'columnvalues' : this.getColumnValues()
			};
			$.each(configProxy.getVisualizations(), function(index, value){
				params[value['class']] = self.getChartedByType(value['class']);
 			});
			return $.extend(params, this.getTimeParams());
		},
		
		getTimeParams : function(){
			return {
				'start' : this.getStart(),
				'end' : this.getEnd(),
				'freq' : this.getFreq()
			}
		},
		
		getFullPath : function(params){
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			return requestProxy.getPath() + '/' + this.getId() + this.getQueryString(params);
		},
		
		update : function(){
			var tmp = {},
			requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			id = requestProxy.getId(),
			topicId = requestProxy.getTopicId(),
			query = requestProxy.getQuery(),
			tmpTopic = configProxy.getTopic(topicId),
			tableParamsUpdated = id !== this.getId(),
			timeParamsUpdated = false,
			chartParamsUpdated =  !('ctype' in requestProxy.getQuery()),
			ctypeParamUpdated = !('ctype' in requestProxy.getQuery()),
			ltypeParamUpdated = false,
			self = this;
			
			this.updateTopicId(topicId);
			this.updateId(id);

			// Now loop through the keys again
			this.updateQuery(requestProxy.getQuery(), function(tmpKey, newVal, oldVal){
				// Check and see if our time parameters have changed
				timeParamsUpdated |= tmpKey == 'start' || tmpKey == 'end' || tmpKey == 'freq';
				// Check and see if our frequency has changed
				tableParamsUpdated |= tmpKey == 'freq';
				$.each(configProxy.getVisualizations(), function(index, value){
					chartParamsUpdated |= tmpKey == value['class'];
				});
				tableParamsUpdated |= tmpKey == 'pin';
				// Check and see if the ctype parameter has changed
				chartParamsUpdated |= ctypeParamUpdated |= tmpKey == 'ctype';
				// Check and see if the chartIndexed parameter has changed
				chartParamsUpdated |= tmpKey == 'chartindexed';
				// Check and see if the columnendpoints parameter has changed
				chartParamsUpdated |= tmpKey == 'columnendpoints';
				// Check and see if the columnvalues parameter has changed
				chartParamsUpdated |= tmpKey == 'columnvalues';
				//Check and see if the Link Type parameter has changed
				ltypeParamUpdated |= tmpKey == 'ltype';
			});
			
			this.updateChartIdFrequencies(this.getFreq());

			if(tableParamsUpdated)
				this.sendNotification(Facade.TABLE_PARAMS_UPDATED);
			if(ctypeParamUpdated){
				if(this.getChartType() != 'pin' && this.getChartType() != 'sourcekey')
					this.sendNotification(Facade.UPDATE_VISUALIZATION_VIEW_STACK);
				this.sendNotification(Facade.CTYPE_PARAM_UPDATED);
			}
			if(chartParamsUpdated)
				this.sendNotification(Facade.CHART_PARAMS_UPDATED);
			if(timeParamsUpdated)
				this.sendNotification(Facade.TIME_PARAMS_UPDATED);
			if(ltypeParamUpdated)
				this.sendNotification(Facade.PIN_KEY_PARAM_UPDATED);
		},
		
		validateQuery : function(vals){
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			topic = configProxy.getTopic(this.getTopicId());
			
			this.validateFrequency(vals, topic);
			this.validateStartEnd(vals, topic);
			this.validateChartParams(vals, topic, configProxy.getVisualizations());
			this.validateLinkType(vals, topic, configProxy.getLinkTypes());
		},

		__classvars__ : {
			NAME : 'ParameterProxy'
		}
	});
});
define('js/model/SiteDataTableDataProxy',[
	'js/Facade',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/model/TableDataMixinLevel',
	'js/model/TableDataMixinChart'
], function(
	Facade,
	ParameterProxy,
	TableDataProxy,
	TableDataMixinLevel,
	TableDataMixinChart
){
	return TableDataProxy.$extend({
		
		isUpdating : false,
		
		__include__ : [
			TableDataMixinLevel,
			TableDataMixinChart
		],
		
		getDataColumns : function(){
			return this.getData()['DATACOLUMNS'];
		},
		
		getMinColumn : function(){
			return this.getData()['DATACOLUMNS'][0];
		},
		
		getMaxColumn : function(){
			return this.getData()['DATACOLUMNS'][this.getData()['dataColumns'].length - 1];
		},
		
		getTableData : function(){
			return this.getData()['DATA'];
		},

		getSeriesData : function(){
			return this.getData()['SERIESDATA']['DATA'];
		},
		
		getCombinedData : function(){
			var ret = [];
			var seriesData = this.getSeriesData();
			var tableTitle = this.getTableTitle();
			var tableData = this.getTableData();
			
			if(seriesData.length > 0)
				ret.push({
					DESCRIPTION: 'Pinned Series',
					ACTION: this.$class.CLEAR
				});
			
			ret = ret.concat(seriesData);
			
			var titleData = {
				DESCRIPTION : tableTitle
			};
			
			if(ret.length == 0) {
				titleData.ACTION = this.$class.CLEAR;
			}
			
			ret.push(titleData);
			
			ret = ret.concat(tableData);
			
			return ret;
		},
		
		getTableTitle : function(){
			return this.getInfo()['NAME'];
		},
		
		getInfo : function(){
			return this.getData()['INFO'];
		},
		
		getChildRows : function(row) {
			var ret = [];
			var combinedData = this.getCombinedData();
			for(var i = row+1; i < combinedData.length && combinedData[i].LEVEL > combinedData[row].LEVEL; i++) { 
				if(combinedData[i].HAS_DATA) ret.push(i);
			}
			
			return ret;
		},
		
		getChildData : function(row){
			var childRows = this.getChildRows(row);
			var combinedData = this.getCombinedData();
			var ret = [];
			for(i=0; i < childRows.length; i++){
				ret.push(combinedData[childRows[i]]);
			}
			return ret;
		},
		
		loadTableData : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableParamsString = parameterProxy.getTableParamsString(),
			self = this;
			
			this.isUpdating = true;
			
			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
				this.processChartData();
				this.isUpdating = false;
				this.sendNotification(Facade.TABLEDATA_UPDATED);
			}
			else{
				$.ajax(
					encodeURI(
						this.$class.PROXY_URL + '?method=' +
						this.$class.PROXY_METHOD + '&' + tableParamsString
					),
					{
						dataType : 'json',
						error : function(){
							self.sendNotification(Facade.ERROR);
						},
						success : function(tableData){
							// extend the table data array with our
							// getRowMetadata function
							tableData.DATA = $.extend(tableData.DATA, {
								'getRowMetadata' : self.$class.getRowMetadata
							});
							tableData.chartData = {};
							self.setData(tableParamsString, tableData);
							self.createIDMap();
							self.processLevels();
							self.processChartData('linechart');
							self.processChartData('columnchart');
							self.processChartData('pin');
							self.isUpdating = false;
							self.sendNotification(Facade.TABLEDATA_UPDATED);
						}
					}
				);
			}
		},
		
		__classvars__ : {
			PROXY_URL : 'data/index.php',
			PROXY_METHOD : 'getPlantData',
			CLEAR : 'clear'
		}
	});
});
define('js/model/MineDataTableDataProxy',[
	'js/Facade',
	'js/model/SiteDataTableDataProxy'
], function(
	Facade,
	SiteDataTableDataProxy
){
	return SiteDataTableDataProxy.$extend({
		
		__classvars__ : {
			PROXY_METHOD : 'getMineData'
		}
	});
});
define('js/view/MediatorMineDataTopicSelection',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/view/MediatorCoalTopicSelection'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	MediatorCoalTopicSelection
) {
	return MediatorCoalTopicSelection.$extend({
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			switch(note.getName()){
				case Facade.TABLE_PARAMS_UPDATED:
					var topicInfo = this.arrangeTopics(configProxy.getTopics(), parameterProxy.getTopicId());
					
					this.getViewComponent().create(
						topicInfo.selectedTopic,
						parameterProxy.getReportIndex(),
						topicInfo.topicGroups,
						configProxy.getReports()
					);
					break;
				default:
					this.$super(note);
			}
		}
	});
});
define('js/view/MediatorPlantDataTimeSelection',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/MediatorDualSliderTimeSelection'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy,
	MediatorDualSliderTimeSelection
) {
	return MediatorDualSliderTimeSelection.$extend({
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			timeParams = parameterProxy.getTimeParams(),
			topic = configProxy.getTopic(parameterProxy.getTopicId()),
			dataColumns;
			
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					dataColumns = tableDataProxy.getDataColumns();
					this.getViewComponent().create(
						$.extend( {}, topic, {
							'selectDateRange' : true
						}),
						timeParams['freq'],
						dataColumns,
						timeParams['start'],
						timeParams['end']
					);
					break;
				default:
					this.$super(note);
			}
		}
	});
});
define('js/view/MediatorPlantDataDataGrid',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'js/view/MediatorAggregateDataGrid',
	'datagrid'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy,
	DateCodeUtil,
	GridFormatters,
	MediatorAggregateDataGrid,
	DataGrid
){
	return MediatorAggregateDataGrid.$extend({
		
		listNotificationInterests : function(){
			var ret = this.$super();
			var index = $.inArray(Facade.RSE_PARAM_UPDATED);
			
			if(index >= 0)
				ret.splice(index, 1);
				
			return ret;
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
					timeParams = parameterProxy.getTimeParams(),
					data = tableDataProxy.getCombinedData(),
					columns = this.generateColumns(
						tableDataProxy.getDataColumns(),
						data,
						timeParams['freq']
					);
					this.getViewComponent().update(
						data, 
						columns, 
						{
							'frozenColumn' : 2,
							'dataItemColumnValueExtractor': function(item, colDef) {
								var accessorArray = (colDef && colDef.field ) ? colDef.field.split('.') : [];
								var ret = (colDef.field ? item : null);
								for(var i = 0; i < accessorArray.length; i++) {
									ret = ret[accessorArray[i]];
									if(!ret || typeof ret == 'undefined')
										break;
								}
								return ret;
							},
							'scrollToRight' : true,
							'addSpacerColumn' : true
						}, 
						timeParams['start'], 
						timeParams['end']
					);
					this.sendNotification(Facade.TABLE_RENDERED);
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		generateColumns : function(dataColumns, data, freq){
			var i, parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			columns = [{
				'id' : 'pinKey',
				'field' : 'HAS_DATA',
				'width' : 30,
				'displayFormatter' : GridFormatters.pinSeriesFormatter,
				'output' : false,
				'cssClass' : 'chart_column',
				'iconClass' : parameterProxy.getLinkType(),
				'resizable' : false
			},{
				'id' : 'description',
				'name' : ' ',
				'field' : 'DESCRIPTION',
				'displayFormatter' : GridFormatters.descriptionFormatter,
				'outputFormatter' : this.$class.descriptionOutputFormatter,
				'cssClass' : 'description',
				'width' : 400,
				'maxWidth' : 904
			},{
				'id' : 'chart',
				'field' : 'HAS_DATA',
				'width' : 30,
				'displayFormatter' : GridFormatters.chartButtonFormatter,
				'output' : false,
				'cssClass' : 'chart_column',
				'iconClass' : parameterProxy.getChartType(),
				'resizable' : false
			},{
				'id' : 'units',
				'name' : 'units',
				'field' : 'UNITS',
				'display' : false
			}];
			
			DataGrid.autosizeColumn(columns, 0, data, 10);
			
			for(i = 0; i < dataColumns.length; i++){
				var columnKey = parseInt(dataColumns[i]).toString();
				columns.push({
					'id' : columnKey,
					'name' : DateCodeUtil.formatDateCode(columnKey, freq),
					'field' : 'DATA.' + columnKey.toString(),
					'dataFormatter' : GridFormatters.numericalDataFormatter,
					'width' : 100
				})
			}
			
			return columns;
		}
	});
});

define('text!templates/mineDataTableTitle.html',[],function () { return '<div>\r\n\t{{#BACKURL}}<a href="./#/{{BACKURL}}">< Return to previous page</a>{{/BACKURL}}\r\n\t<br/>\r\n\t<strong>Mine name:</strong> {{NAME}}  |  <strong>MSHA id:</strong> {{ID}} \r\n\t<br/>\r\n\t<strong>State:</strong> {{STATE}}  |  <strong>Status:</strong> {{STATUS}} \r\n\t{{#LON}} |  <a target="_blank" href="https://eia.maps.arcgis.com/apps/webappviewer/index.html?id={{WEBAPP}}&webmap={{MAP}}¢er={{LON}},{{LAT}}&level={{LEVEL}}&showLayers={{MAPLAYERS}}">View in dynamic map viewer</a>{{/LON}} \r\n\t |  Coal shipments from {{NAME}}\r\n</div>';});

define('js/pages/MineDataPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/SiteDataConfigProxy',
	'js/model/SiteDataRequestProxy',
	'js/model/SiteDataParameterProxy',
	'js/model/MineDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/FootnoteProxy',
	'js/view/VC_PageLayout',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'datagrid',
	'viewStack',
	'js/view/VC_ColumnChart',
	'js/view/VC_LineChart',
	'qTipsHelp',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorMineDataTopicSelection',
	'js/view/MediatorPlantDataTimeSelection',
	'js/view/MediatorPlantDataDataGrid',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorPinKeyVisualization',
	// Templates
	'text!templates/main.html',
	'text!templates/filter_button.html',
	'text!templates/download_button.html',
	'text!templates/mineDataTableTitle.html',
	'text!templates/footnotes.html'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	FootnoteProxy,
	TopicPageLayout,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	TimeSelectionDialog,
	DataGrid,
	ViewStack,
	ColumnChart,
	LineChart,
	HelpTooltipDialog,
	DownloadMediator,
	VisualizationMediator,
	TopicSelectionMediator,
	TimeSelectionMediator,
	DataGridMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	HelpTooltipMediator,
	PinKeyVisualizationMediator,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplate,
	footnoteTemplate
){
	var facade = null;

	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			request["0"] = 38;
			$('#pagecontent').addClass('plantData');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = PlantDataFacade.getInstance(this.name);
				facade.sendNotification(PlantDataFacade.STARTUP, request);
			}
			else
				facade.sendNotification(PlantDataFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(PlantDataFacade.UNLOAD);
			PlantDataFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('plantData');
		}
	})('js/pages/MineDataPage');
	
	////////////
	// Facade //
	////////////
	
	var PlantDataFacade = Facade.$extend({
		
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.CHART_PARAMS_UPDATED, Controller.UpdateChartDataCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, TableSelectionUpdatedCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.DEFAULT_CHART_SET, Controller.UpdateChartedCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.SOURCE_KEY_SELECTED, Controller.OpenAPIWindowCommand);
			this.registerCommand(this.$class.PIN_KEY_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		 __classvars__ : {
			
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new PlantDataFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
			
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableParameter = {
				'id' : 'csv_table',
				'type' : 'csv',
				'class' : 'ico_csv',
				'target' : 'table',
				'label' : 'Table (CSV)'
			};
			var requestProxy = new RequestProxy(note.getBody());
			var configProxy = new ConfigProxy();
			configProxy.setDownloadParameters(
				{
					'columnchart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'columnchart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'columnchart',
									label : 'PDF'
								},
								{
									id : 'columnchart_png',
									type : 'png',
									'class': 'ico_img',
									target : 'columnchart',
									label : 'PNG'
								},
                                {
                                    id : 'columnchart_svg',
                                    type : 'svg',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'columnchart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'columnchart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'columnchart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'columnchart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'linechart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'linechart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'linechart',
									label : 'PDF'
								},
								{
									id : 'linechart_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'linechart',
									label : 'PNG'
								},
                                {
                                    id : 'linechart_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'linechart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'linechart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'linechart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'linechart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					]
				}
			);
			var visualizations = VisualizationDialog.defaultConfig.items.slice(); 
			visualizations.splice(2, 1);
			configProxy.setVisualizations(visualizations);
			configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
			if(page.$class.requests.length > 1) {
				configProxy.setPreviousPage(page.$class.requests[page.$class.requests.length-2]);
			}
			
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(footnoteProxy);
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TableSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	
	/////////////
	// Proxies //
	/////////////
	
	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var pageLayout = this.getViewComponent();
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), {items : configProxy.getVisualizations()});
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			var dataGrid = new DataGrid(pageLayout.getTableContainer());
			var viewStack = new ViewStack(pageLayout.getChartContainer());
			var pinKeyVisualizationDialog = new VisualizationDialog(pageLayout.getPinKeyVisualizationContainer(), {items : configProxy.getLinkTypes()});

 			
			var lineChartContainer = viewStack.addView('linechart');
			var columnChartContainer = viewStack.addView('columnchart');
			
			var columnChart = new ColumnChart(columnChartContainer);
			var lineChart = new LineChart(lineChartContainer);
			var helpTooltipDialog = new HelpTooltipDialog();

			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var middleTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var topTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var viewStackMediator = new ViewStackMediator(viewStack);
			var lineChartMediator = new LineChartMediator(lineChart);
			var columnChartMediator = new ColumnChartMediator(columnChart);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var pinKeyVisualizationMediator = new PinKeyVisualizationMediator(pinKeyVisualizationDialog);

			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);

			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : true,
				showTransportationMode : false,
				showPrimeMovers : false,
				showPlantFuelTypes : false,
				showMineType : false,
				showMineStatus : false
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate,  {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}));
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
			
			
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.TABLE_RENDERED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();
			switch(note.getName()){
				case Facade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var prevPage = configProxy.getPreviousPage();
					
					vc.getTitleContainer().empty();
					vc.getTitleContainer().css('display', 'block');
				
				
					this.getMapUrl(vc, tableDataProxy, prevPage, parameterProxy);
					
					window.document.title = 'Coal Data Browser - ' + tableDataProxy.getTableTitle();

					break;
				case Facade.TABLE_RENDERED:
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},

		getMapUrl: function(vc, tableDataProxy, prevPage, parameterProxy){
			//perform ajax call to layer-config.php script.
			let mapId = 'coal-map';
			let url = window.location.protocol + '//' + window.location.hostname + '/global/scripts/maps/layer-config.php';
			let layers = '';
			let zoomLevel, webapp, webmap = null;
			$.ajax(
				encodeURI(url + '?mapId=' + mapId + '&level=1;'	
				),
				{
					dataType : 'json',
					error : function(){
						self.sendNotification(Facade.ERROR);
					},
					success : function(data, textStatus, jqXHR) {
						var filteredArray = data.mapId.filter(function(e) { return e !== 'State Mask' })
						for(let layer of filteredArray){
							console.log(layer);
							layers += layer + ';';
						}
						zoomLevel = data.level;
						webapp = data.webapp;
						webmap = data.webmap;
						console.log(webapp);
						console.log(webmap);
						console.log(zoomLevel);
					var linkArgs = parameterProxy.getQueryString({'ctype':''});
					vc.getTitleContainer().append(Mustache.to_html(tableTitleTemplate, $.extend(tableDataProxy.getInfo(), {BACKURL:(prevPage ? prevPage.request_ : parameterProxy.getFullTopicPath({}, parameterProxy.getTopicId())), LINK_ARGS : linkArgs, LEVEL: zoomLevel, MAPLAYERS : layers, WEBAPP : webapp, MAP: webmap })));

						
					}
				}
			);
			//pass in the layers to load


		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});

	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'disabled' : true,
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainVisualizationButtonOpts' : {
					'selected' : false,
					'id' : 'main_visualization_button'
				},
				'overlayVisualizationButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_visualization_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
		}
	});
	
	return page;
});
define('js/view/MediatorMinePlantSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ParameterProxy
) {
	return  puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent, top){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this;
			this.getViewComponent().selectionChanged.add(function(topicId){
				self.sendNotification(Facade.TOPIC_SELECTION_UPDATED, {'topic' : topicId}, 'TopicChanged');
			});
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			this.getViewComponent().create(
				parameterProxy.getTopicId()
			);
			
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.TABLE_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			switch(note.getName()){
				case Facade.TABLE_PARAMS_UPDATED:
					this.getViewComponent().updateSelection(
						parameterProxy.getTopicId()
					);
					break;
				default:
					this.$super(note);
			}
		},
		
		__classvars__ : {
			NAME : 'MinePlantSelectionMediator'
		}
	});
});

define('text!templates/minePlantSelection.html',[],function () { return '<div id="mine_plant_button" class="ui-buttonset">\r\n\t<input type="radio" id="mine-radio" name="mine_plant" value="mine"/>\r\n\t<label for="mine-radio" alt="Mine" title="Mine" class="ui-button">By mine state</label>\r\n\t<input type="radio" id="plant-radio" name="mine_plant" value="plant"/>\r\n\t<label for="plant-radio" alt="Plant" title="Plant" class="ui-button">By plant state</label>\r\n</div>';});

define('js/view/VC_MinePlantSelection',[
	'classy',
	'signals',
	'text!templates/minePlantSelection.html',
],
function(Class, signals, minePlantSelectorTemplate)
{
	return Class.$extend({
		
		minePlantSelect : null,

		selectionChanged : null,
		
		topicId : null,
		
		__init__ : function(container){
			this.container = container;

			this.selectionChanged = new signals.Signal();
		},
		
		create : function(topicId){
			var self = this;
			
			this.topicId = topicId;
			
			this.container.html(
				Mustache.render(minePlantSelectorTemplate, {})
			);
			this.minePlantSelect = $('input[name="mine_plant"]', this.container)
			.change(function(){
				if(self.minePlantSelect.filter(':checked').val() === 'mine'){
					self.topicId -= 1;
				}
				else if(self.minePlantSelect.filter(':checked').val() === 'plant'){
					self.topicId += 1;
				}
				self.selectionChanged.dispatch(self.topicId);
			});
			$('#mine_plant_button').buttonset();
			this.updateSelection(topicId);			
		},
		
		updateSelection : function(topicId){
			var mineVals = [44, 46, 48, 50, 52, 54];
			var plantVals = [45, 47, 49, 51, 53, 55];
			
			this.topicId = topicId;
			
			var type = "";
			if($.inArray(topicId, mineVals) != -1){
				type = "mine";
			} else if($.inArray(topicId, plantVals) != -1)  {
				type = "plant";
			}
			this.minePlantSelect.filter('[value="' + type + '"]').attr('checked', true);
			$('#mine_plant_button').buttonset('refresh');
		}
	});
});
define('js/pages/AggregateShipmentPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	//Facade
	'js/Facade',
	'js/Controller',
	//Model
	'js/model/AggregateDataConfigProxy',
	'js/model/AggregateDataRequestProxy',
	'js/model/AggregateDataParameterProxy',
	'js/model/AggregateDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/MapDataProxy',
	'js/model/FootnoteProxy',
	//View
	'js/view/MediatorCoalTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorPinKeyVisualization',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorDualSliderTimeSelection',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorAnimatedMap',
	'js/view/MediatorAnalysis',
	'js/view/MediatorAggregateDataGrid',
	'js/view/MediatorMinePlantSelection',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'js/view/VC_Analysis',
	'js/view/VC_LineChart',
	'js/view/VC_ColumnChart',
	'js/view/VC_AnimatedMap',
	'js/view/VC_MinePlantSelection',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'datagrid',
	'qTipsHelp',
	'viewStack',
	// Templates
	'text!templates/tableTitle.html',
	'text!templates/footnotes.html',
	// JS and CSS
	'simpleModal'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	MapDataProxy,
	FootnoteProxy,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	VisualizationMediator,
	PinKeyVisualizationMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	TimeSelectionMediator,
	HelpTooltipMediator,
	AnimatedMapMediator,
	AnalysisMediator,
	DataGridMediator,
	MinePlantSelectionMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	TimeSelectionDialog,
	AnalysisDialog,
	TopicLineChart,
	TopicColumnChart,
	EnerdatAnimatedMap,
	MinePlantSelection,
	dateCodeUtils,
	GridFormatters,
	DataGrid,
	HelpTooltipDialog,
	ViewStack,
	tableTitleTemplate,
	footnoteTemplate
){
	var facade = null;
	
	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			$('#pagecontent').addClass('aggregate');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = AggregateDataFacade.getInstance(this.name);
				facade.sendNotification(AggregateDataFacade.STARTUP, request);
			}
			else
				facade.sendNotification(AggregateDataFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(AggregateDataFacade.UNLOAD);
			AggregateDataFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('aggregate');
		}
	})('js/pages/AggregateShipmentPage');
	
	////////////
	// Facade //
	////////////
	
	var AggregateDataFacade = Facade.$extend({
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.CHART_PARAMS_UPDATED, Controller.UpdateChartDataCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, TableSelectionUpdatedCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_DATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.TABLEDATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.DEFAULT_CHART_SET, Controller.UpdateChartedCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.MAP_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.RSE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.AGGREGATE_DATA_CLICK_THROUGH, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.SOURCE_KEY_SELECTED, Controller.OpenAPIWindowCommand);
			this.registerCommand(this.$class.PIN_KEY_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.REGION_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		
		__classvars__ : {
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new AggregateDataFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var configProxy = new ConfigProxy();
			var requestProxy = new RequestProxy(note.getBody());
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var mapDataProxy = new MapDataProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(mapDataProxy);
			this.facade.registerProxy(footnoteProxy);

            var tableParameter = {
                id : 'csv_table',
                type : 'csv',
                'class' : 'ico_csv',
                target : 'table',
                label : 'Table (CSV)'
            };

            configProxy.setVisualizations(VisualizationDialog.defaultConfig.items.slice());
            configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
            configProxy.setDownloadParameters(
                {
                    'map' : [
                        {
                            label : 'Map',
                            children : [
                                {
                                    id : 'map_png',
                                    type : 'png',
                                    'class' : 'ico_img',
                                    target : 'map',
                                    label : 'Map (PNG)'
                                },
                                {
                                    id : 'map_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'map',
                                    label : 'Map (PDF)'
                                },
                                {
                                    id : 'map_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'map',
                                    label : 'Map (SVG)'
                                },
                                {
                                    id : 'map_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'map',
                                    label : 'Map Embed'
                                }
                            ]
                        },
                        {
                            label : 'Data',
                            children : [
                                {
                                    id : 'map_csv',
                                    type : 'csv',
                                    'class' : 'ico_csv',
                                    target : 'map',
                                    label : 'Map (CSV)'
                                },
                                tableParameter
                            ]
                        }
                    ],

                    'columnchart' : [
                        {
                            label : 'Chart',
                            children : [
                                {
                                    id : 'columnchart_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'columnchart',
                                    label : 'PDF'
                                },
                                {
                                    id : 'columnchart_png',
                                    type : 'png',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'PNG'
                                },
                                {
                                    id : 'columnchart_svg',
                                    type : 'svg',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'columnchart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'columnchart',
                                    label : 'EMBED'
                                }
                            ]
                        },
                        {
                            label : 'Data',
                            children : [
                                {
                                    id : 'columnchart_csv',
                                    type : 'csv',
                                    'class' : 'ico_csv',
                                    target : 'columnchart',
                                    label : 'Chart (CSV)'
                                },
                                tableParameter
                            ]
                        }
                    ],

                    'linechart' : [
                        {
                            label : 'Chart',
                            children : [
                                {
                                    id : 'linechart_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'linechart',
                                    label : 'PDF'
                                },
                                {
                                    id : 'linechart_png',
                                    type : 'png',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'PNG'
                                },
                                {
                                    id : 'linechart_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'linechart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'linechart',
                                    label : 'EMBED'
                                }
                            ]
                        },
                        {
                            label : 'Data',
                            children : [
                                {
                                    id : 'linechart_csv',
                                    type : 'csv',
                                    'class' : 'ico_csv',
                                    target : 'linechart',
                                    label : 'Chart (CSV)'
                                },
                                tableParameter
                            ]
                        }
                    ]
                }
            );
			
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TableSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			//this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			//this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.ResetAggColumnsCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	var ChartDataUpdatedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
			
			if(parameterProxy.getChartType() == 'map'){
				mapDataProxy.updateMapData(tableDataProxy.getChartData('map')[0]);
			}
		}
	});

	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var self = this; 
			
			var pageLayout = this.getViewComponent();

			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), {items : configProxy.getVisualizations()});
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			//var analysisDialog = new AnalysisDialog(pageLayout.getAnalysisContainer());
			var dataGrid = new DataGrid(pageLayout.getTableContainer());
			var viewStack = new ViewStack(pageLayout.getChartContainer());
			var pinKeyVisualizationDialog = new VisualizationDialog(pageLayout.getPinKeyVisualizationContainer(), {items : configProxy.getLinkTypes()});
			
			var lineChartContainer = viewStack.addView('linechart');
			var columnChartContainer = viewStack.addView('columnchart');
			var mapContainer = viewStack.addView('map');
			
			var columnChart = new TopicColumnChart(columnChartContainer);
			var lineChart = new TopicLineChart(lineChartContainer);
			var helpTooltipDialog = new HelpTooltipDialog();
			var animatedMap = new EnerdatAnimatedMap(mapContainer);
			
			var formMediator = new FormMediator(formDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var topTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var viewStackMediator = new ViewStackMediator(viewStack);
			var lineChartMediator = new LineChartMediator(lineChart);
			var columnChartMediator = new ColumnChartMediator(columnChart);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var animatedMapMediator = new AnimatedMapMediator(animatedMap);
			//var analysisMediator = new AnalysisMediator(analysisDialog);
			var pinKeyVisualizationMediator = new PinKeyVisualizationMediator(pinKeyVisualizationDialog);
			
			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(formMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(animatedMapMediator);
			//this.facade.registerMediator(analysisMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);
			
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : false,
				showTransportationMode : false,
				showPrimeMovers : false,
				showPlantFuelTypes : false,
				showMineType : false,
				showMineStatus : false
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}))
				.on('change', '#rse_checkbox', function(e){
					self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
						'rse' : $(e.target).is(':checked') ? '1' : '0'
					});
				});
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});

			var minePlantSelection = new MinePlantSelection(pageLayout.getMinePlantSelectionContainer(), { topicId : requestProxy.getTopicId() });
			var minePlantSelectionMediator = new MinePlantSelectionMediator(minePlantSelection);
			this.facade.registerMediator(minePlantSelectionMediator);
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				AggregateDataFacade.TABLEDATA_UPDATED,
				AggregateDataFacade.RSE_PARAM_UPDATED,
				AggregateDataFacade.TABLE_RENDERED,
				AggregateDataFacade.TOPIC_PARAMS_UPDATED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);

			switch(note.getName()){
				case AggregateDataFacade.TABLEDATA_UPDATED:
					var pageTitle = tableDataProxy.getTableTitle();
					if(parameterProxy.getReportIndex() != null) {
						pageTitle = configProxy.getReports()[parameterProxy.getReportIndex()].description;
					}
					
					window.document.title = 'Coal Data Browser - ' + pageTitle;
					break;
				case AggregateDataFacade.TABLE_RENDERED:
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				case AggregateDataFacade.RSE_PARAM_UPDATED:
					vc.getFootnoteContainer()
						.find('#rse_checkbox')
						.attr('checked', parameterProxy.getShowRSE()); 
					break;
				case AggregateDataFacade.TOPIC_PARAMS_UPDATED:
					var self = this;
					var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, {
							urlArgs : footnoteProxy.getUrlArgs(), 
							footnoteText : footnoteProxy.getFootnotes()
						}))
						.on('change', '#rse_checkbox', function(e){
							self.sendNotification(AggregateDataFacade.RSE_SELECTION_UPDATED, {
								'rse' : $(e.target).is(':checked') ? '1' : '0'
							});
						});
						
					$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});
	

	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainVisualizationButtonOpts' : {
					'selected' : false,
					'id' : 'main_visualization_button'
				},
				'overlayVisualizationButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_visualization_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
		}
	});
	var EnerdatAnimatedMap = EnerdatAnimatedMap.$extend({
		__init__ : function(container) {
			this.$super(container);
			this.ContinuousMapClass = this.ContinuousMapClass.$extend({
				createLegend : function() {
					this.$super();
					$('#map_legend', this.container).append('<span class="marker_disclaimer">Data only includes shipments to the electric power sector.</span>');
				}
			});
			this.GrowthMapClass = this.GrowthMapClass.$extend({
				createLegend : function() {
					this.$super();
					$('#map_legend', this.container).append('<span class="marker_disclaimer">Data only includes shipments to the electric power sector.</span>');
				}
			});
		}
	});
	return page;
});
define('js/pages/ShipmentListPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/ListConfigProxy',
	'js/model/ListParameterProxy',
	'js/model/ListRequestProxy',
	'js/model/ListTableDataProxy',
	'js/model/HelpToolTipProxy',
	'js/model/FootnoteProxy',
	'js/view/MediatorCoalTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorDownload',
	'js/view/MediatorSearch',
	'js/view/MediatorSingleSliderTimeSelection',
	'js/view/MediatorMineListMap',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorMineListDataGrid',
	'js/view/MediatorMinePlantSelection',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Search',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'js/view/VC_MineListMap',
	'qTipsHelp',
	'js/view/VC_DataViewGrid',
	'js/view/VC_MinePlantSelection',
	'js/view/DateCodeUtil',
	// Templates
	'text!templates/main.html',
	'text!templates/filter_button.html',
	'text!templates/download_button.html',
	'text!templates/tableTitle.html',
	'text!templates/map.html',
	'text!templates/footnotes.html',
	'slickdataview',
	'us-merc-en'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	ParameterProxy,
	RequestProxy,
	TableDataProxy,
	HelpTooltipProxy,
	FootnoteProxy,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	SearchMediator,
	TimeSelectionMediator,
	ListMapMediator,
	HelpTooltipMediator,
	DataGridMediator,
	MinePlantSelectionMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	SearchBox,
	TopicSelectionDialog,
	TimeSelectionDialog,
	MineListMap,
	HelpTooltipDialog,
	DataViewGrid,
	MinePlantSelection,
	dateCodeUtils,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplate,
	mapTemplate,
	footnoteTemplate
){
	var facade = null;
	
	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			$('#pagecontent').addClass('mineList');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = MineListFacade.getInstance(this.name);
				facade.sendNotification(MineListFacade.STARTUP, request);
			}
			else
				facade.sendNotification(MineListFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(MineListFacade.UNLOAD);
			MineListFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('mineList');
		}
	})('js/pages/ShipmentListPage');
	
	////////////
	// Facade //
	////////////
	
	var MineListFacade = Facade.$extend({
		
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.SEARCH_STRING_UPDATED, UpdateMineListSearchCommand);
			this.registerCommand(this.$class.STATE_MAP_CLICKED, UpdateStateFilterCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.SHIPMENT_LIST_CLICK_THROUGH, Controller.UpdateShipmentHashCommand);
			this.registerCommand(this.$class.REGION_TYPE_SELECTION_UPDATED, Controller.UpdateHashCommand);
		},
		
		__classvars__ : {
			
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new MineListFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
		}
	});
	//////////////
	// Proxies ///
	//////////////
	var TableDataProxy = TableDataProxy.$extend({
		getMineMarkers : function() {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			var data = this.getData()['DATA'];
			var ret = [];
			
			if(parameterProxy.isMine()) {
				ret = this.$super();
				$.each(data, function(index, value) {
					ret[index]['Average Ash Content'] = value['Average Ash Content'];
					ret[index]['Average Sulfur Content'] = value['Average Sulfur Content'];
					ret[index]['Average Heat Content'] = value['Average Heat Content'];
					ret[index]['Receipts Tons'] = value['Coal Received by Electric Power Plants (short tons)'];
					ret[index]['Average Price Per Ton'] = value['Average Price Per Ton'];
				});
			}
			else {
				var mineMarker = this.facade.retrieveMediator(ListMapMediator.NAME).getViewComponent().$class.MINE_MARKER;
				
				$.each(data, function(index, value) {
					var tmp = {
						latLng : [ value['lat'], value['lon']],
						name : value['Plant Name'],
						code : value['Plant Code'],
						primeMovers : value['Prime Movers'],
						sector : value['Sector Name'],
						fuelTypes : value['Fuel Types'],
						'Average Ash Content' : value['Average Ash Content'],
						'Average Price Per Ton' : value['Average Price Per Ton'],
						'Receipts Tons' : value['Coal Receipts (short tons)'],
						'Average Heat Content' : value['Average Heat Content'],
						'Average Sulfur Content' : value['Average Sulfur Content']
					};

					var fuelType = value['Aggregate Fuel Type'];

					if (mineMarker.hasOwnProperty(fuelType)) {
						tmp['style'] = {
							fill: mineMarker[value['Aggregate Fuel Type']].color
						};
					};

					ret.push(tmp);
				});
			}
			
			return ret;
		},
		
		__classvars__ : {
			PROXY_URL : 'data/index.php',
			PROXY_METHOD : 'getShipmentList'
		}
	});
	
	var ParameterProxy = ParameterProxy.$extend({
		isMine : function() {
			return this.facade.retrieveProxy(RequestProxy.NAME).getTopicId() == 54;
		},
		
		isPlant : function() {
			return this.facade.retrieveProxy(RequestProxy.NAME).getTopicId() == 55;
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var requestProxy = new RequestProxy(note.getBody());
			var configProxy = new ConfigProxy();
			configProxy.setDownloadParameters(
				[
					{
						label : 'Data',
						children : [
							{
								id : 'csv_table',
								type : 'csv',
								'class' : 'ico_csv',
								target : 'table',
								label : 'Table (CSV)'
							}
						]
					}
				]
			);
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(footnoteProxy);
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
	
	var FrequencySelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetDatecodeCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var UpdateMineListSearchCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			tableDataProxy.updateListFilter(note.getBody());
		}
	});
	
	var UpdateStateFilterCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
			parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			stateCode = note.getBody().stateCode, filterArray = [], filter = {geo:''}, binaryString = "";
			
			var topic = configProxy.getTopic(parameterProxy.getTopicId());
			
			for(var i = 0; i < topic.aggColumns.length; i++) {
				if(topic.aggColumns[i].filterGroup.varName == 'geo') {
					var filterValues = topic.aggColumns[i].filterGroup.filterValues;
					for(var j = 0; j < filterValues.length; j++) {
						if(filterValues[j].filterValue == stateCode) {
							filterArray[filterValues[j].valueId] = 1;
							break;
						}
					}
					break;
				}
			}
			
			for(i = 0; i < Math.ceil(filterArray.length / 5) * 5; i++) {
				if(filterArray[i] && filterArray[i] == 1) {
					binaryString += "1";
				}
				else {
					binaryString += "0";
				}
				if((i % 5) == 4) {
					filter['geo'] = filter['geo'] + parseInt(binaryString, 2).toString(32);
					binaryString = "";
				}
			}
			filter['geo'] = (filter['geo'] == "") ? "g" : filter['geo'];
			
			this.sendNotification(Facade.TABLE_SELECTION_UPDATED, filter, 'StateMapCicked');
		}
	});
	

	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var pageLayout = this.getViewComponent();
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			pageLayout.getPinKeyVisualizationContainer().css('display', 'none');
			
			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var searchBox = new SearchBox(pageLayout.getSearchContainer());
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var dataGrid = new DataViewGrid(pageLayout.getTableContainer());
			var helpTooltipDialog = new HelpTooltipDialog();
			var listMap = new MineListMap(pageLayout.getChartContainer(), 'us_merc_en');
			var minePlantSelection = new MinePlantSelection(this.getViewComponent().getMinePlantSelectionContainer(), { topicId : parameterProxy.getTopicId() });
						
						
			var formMediator = new FormMediator(formDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var searchMediator = new SearchMediator(searchBox);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var topTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var listMapMediator = new ListMapMediator(listMap);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var minePlantSelectionMediator = new MinePlantSelectionMediator(minePlantSelection);
			
			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(formMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(searchMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(listMapMediator);
			this.facade.registerMediator(minePlantSelectionMediator);
			
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : true,
				showTransportationMode : false,
				showPrimeMovers : parameterProxy.isPlant(),
				showPlantFuelTypes : parameterProxy.isPlant(),
				showMineType : parameterProxy.isMine(),
				showMineStatus : parameterProxy.isMine()
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}));
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.RELOAD,
				Facade.TABLEDATA_UPDATED,
				Facade.TABLE_RENDERED
			]);
		},
		
		handleNotification : function(note){
			switch(note.getName()){
				case Facade.RELOAD:
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
					footnoteProxy.setData({
						rseAvailable : false,
						showFilterValues : true,
						showDetail : true,
						showTransportationMode : false,
						showPrimeMovers : parameterProxy.isPlant(),
						showPlantFuelTypes : parameterProxy.isPlant(),
						showMineType : parameterProxy.isMine(),
						showMineStatus : parameterProxy.isMine()
					});
					
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, {
							urlArgs : footnoteProxy.getUrlArgs(), 
							footnoteText : footnoteProxy.getFootnotes()
						}));
					$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
					break;
				case Facade.TABLEDATA_UPDATED:
					var vc = this.getViewComponent();
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					
					var pageTitle = tableDataProxy.getTableTitle();
					
					this.getViewComponent().getTitleContainer().empty();
					this.getViewComponent().getTitleContainer().css('display', 'block');
					this.getViewComponent().getTitleContainer().append(Mustache.to_html(tableTitleTemplate, {title:pageTitle}));
					
					window.document.title = 'Coal Data Browser - ' + pageTitle;
					break;
				case Facade.TABLE_RENDERED:
					var vc = this.getViewComponent();
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});

	var DataGridMediator = DataGridMediator.$extend({
		onRegister : function() {
			this.$super();
			
			var self = this;
			
			this.getViewComponent().onClick.removeAll();
			this.getViewComponent().onClick.add(function(row, cell, datum) {
				var vc = self.getViewComponent(), columns = vc.columns;
				var chartType = columns[cell].iconClass;
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var columnDef;
				var type;
				var id;
				
				if(parameterProxy.isMine()) {
					type = 'mine';
					id = datum["MSHA ID"];
				}
				else {
					type = 'plant';
					id = datum["Plant Code"];
				}
				
				if(cell === 0){
					var freq = parameterProxy.getTimeParams()['freq'];
					var label = 'shpiments/' + type + "/" + id + '?freq=' + freq;
					
					var body = {
						label : label,
						code : id,
						freq : freq,
						type : type
					};

					self.sendNotification(Facade.SHIPMENT_LIST_CLICK_THROUGH, body, 'ShipmentListClickThrough');
				}
			});
		}
	});
	
	var ListMapMediator = ListMapMediator.$extend({
		markerLabelShow : function(event, label, code) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var labelTxt = "";
			var vc = this.getViewComponent();
			var value = vc.options.markers[code];
			
			if(parameterProxy.isMine()) {
				labelTxt = this.$super(event, label, code) + '<BR>' +
					'Shipments quantity: ' + Highcharts.numberFormat(value['Receipts Tons'], self.precision) + ' tons'
			}
			else {
				
				labelTxt = value['name'] + '<BR>' +
					'Plant code: ' + value['code'] + '<BR>' +
					'Sector: ' + value['sector'] + '<BR>' + 
					'Fuel types: ' + value['fuelTypes'] + '<BR>' +
					'Prime movers: ' + value['primeMovers'] + '<BR>' +
					'Location: ' + value['latLng'][0] + ', ' + value['latLng'][1] + '<BR>' +
					'Shipments quantity: ' + Highcharts.numberFormat(value['Receipts Tons'], self.precision) + ' tons';
			}
			$(label).html(labelTxt);
		},
		
		onRegionLabelShow : function(event, label, code) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var vc = this.getViewComponent();
			var value = vc.getCurrentCounts()[code];
			
			if(parameterProxy.isMine()) {
				return this.$super(event, label, code);
			}
			else {
				if ((parameterProxy.getRegionType() == 'b' && code.split('-')[0] == 'BS') || (parameterProxy.getRegionType() != 'b' && code.split('-')[0] == 'US')) {
					$(label).html($(label).html() + ' ' + (value !== void 0 ? value : 0) + ' plants');
				}
			}
		},
		
		getLegendTitle : function() {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			if(parameterProxy.isMine()) {
				return this.$super();
			}
			else {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			return tableDataProxy.getMineMarkers().length + ' total plants';
			}
		},
		
		markerClickHandler : function(marker, event) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var freq = parameterProxy.getTimeParams()['freq'];
			var type;
			
			if(parameterProxy.isMine()) {
				type = 'mine';
			}
			else {
				type = 'plant';
			}
			
			var label = 'shpiments/' + type + "/" + marker['code'] + '?freq=' + freq;
					
			var body = {
				label : label,
				code : marker['code'],
				freq : freq,
				type : type
			};

			this.sendNotification(Facade.SHIPMENT_LIST_CLICK_THROUGH, body, 'ShipmentListClickThrough');
		
		},
		
		getMarkers : function() {
			return this.$super().sort(function(a, b) {
				return b['Receipts Tons'] - a['Receipts Tons'];
			});
		}
	});

	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
			this.getChartContainer().html(Mustache.render(mapTemplate));
		}
	});
	
	var MineListMap = MineListMap.$extend({
		createMarkerOps : function() {
			var markerValues = [], min = null, max = null;
			
			if(this.options.markers != null) {
				$.each(this.options.markers, function(index, value) {
					if(typeof value['latLng'] == 'object' && value['latLng'].length == 2 && value['latLng'].join('') != '') {
						markerValues.push(value['Receipts Tons']);
						min = ((min == null) ? value['Receipts Tons'] : Math.min(min, value['Receipts Tons']));
						max = ((max == null) ? value['Receipts Tons'] : Math.max(max, value['Receipts Tons']));
					}
				});
			}
			
			return [{
				attribute: 'r',
				scale : [5, 15],
				values : markerValues,
				min : min,
				max : max
			}];
		},
		
		createLegend : function() {
			this.$super();
			$('#map_legend', this.container).append('<span class="marker_disclaimer">Data only includes shipments to the electric power sector.</span>');
		}
	});
	return page;
});

define('text!templates/networkMap.html',[],function () { return '<h2 id="mapTitle"></h2>\r\n<div id="mapContainer"></div>\r\n<div id="controls_container">\r\n\t<h2>Map controls & legend</h2>\r\n\t<div class="animated_map_button_container">\r\n\t\t<div id="animated_map_slider"></div>\r\n\t\t<br/>\r\n\t\t<div id="animated_map_transport_container">\r\n\t\t\t<input type="checkbox" id="play_button"></input><label for="play_button">Play</label>\r\n\t\t\t<input type="checkbox" id="pause_button"></input><label for="pause_button">Pause</label>\r\n\t\t\t<button id="reset_button">Reset</button>\r\n\t\t</div>\r\n\t\t<hr>\r\n\t\t<div id="map_units_label_container">\r\n\t\t\t<span id="map_units_label"></span>\r\n\t\t</div>\r\n\t\t<div id="map_legend"></div>\r\n\t</div>\r\n</div>\r\n<div id="mapNotes" class="clear"></div>\r\n';});

define('js/view/VC_NetworkMap',[
	'map',
	'continuousMap',
	'js/view/VC_AnimatedMap',
	'js/view/DateCodeUtil', 
	'js/view/VC_MineListMap',
	'text!templates/networkMap.html',
	'signals'
],function(
	Map,
	ContinuousMap,
	AnimatedMap,
	DateCodeUtil,
	MineListMap, 
	mapTemplate,
	signals
){
	return AnimatedMap.$extend({
		__init__ : function(container){
			var self = this;
			
			Map = Map.$extend({
				getCurrentCounts : function(){
					return self.markerValues[this.animation.iteration];
				},
				
				createMarkerOps : function() {
					var values = this.getCurrentCounts();
					var qtyMin, qtyMax, dataMin, dataMax;
					
					$.each(self.markerValues, function(iteration, markerVals) {
						$.each(self.markerValues[iteration][self.data.DATA.qtyType], function(index, value) {
							if(value != null) {
								qtyMin = ((typeof qtyMin == 'undefined') ? value : Math.min(qtyMin, value));
								qtyMax = ((typeof qtyMax == 'undefined') ? value : Math.max(qtyMax, value));
							}
						});
						$.each(self.markerValues[iteration][self.data.DATA.type], function(index, value) {
							if(value != null) {
								dataMin = ((typeof dataMin == 'undefined') ? value : Math.min(dataMin, value));
								dataMax = ((typeof dataMax == 'undefined') ? value : Math.max(dataMax, value));
							}
						});
					});
					
					return [{
						attribute: 'r',
						scale : [5, 15],
						values : values[self.data.DATA.qtyType],
						min : qtyMin,
						max : qtyMax
					},{
						attribute: 'fill',
						scale : self.$class.palette[self.data.DATA.type],
						values : values[self.data.DATA.type],
						min : dataMin,
						max : dataMax
					}];
				},
				
				createRegionOps : function() {
					return null;
				}
			});
			
			this.container = container;
			this.container.addClass('network');
			this.container.html(Mustache.render(mapTemplate));
			
			this.createPlayerControls();
			
			this.unitsLabel = $('#map_units_label', this.container);
			
			this.paths = [];

			this.markerClicked = new signals.Signal();
			
			this.focus = null;
		},
		
		unDrawNetwork : function() {
			$.each(this.paths, function(index, value) {
				value.remove();
			});
			this.paths = [];
		},
		
		drawNetwork : function(period) {
			if(this.map && this.map.worldMap) {
				var markers = this.map.worldMap.markers;
				var g = this.map.worldMap.regionsGroup;
				var centerNode;
				
				this.unDrawNetwork();
				
				$.each(markers, function(key, value) {
					if(value.config.centerNode) {
						centerNode = value;
					}
				});
				var colors = [];
				if(typeof centerNode != 'undefined') {
					var self = this;
					
					$.each(markers, function(key, value) {
						if(value != centerNode) {
							var path = 'M' + 
								((centerNode.element.properties.cx / self.map.worldMap.scale) - self.map.worldMap.transX) + ',' +
								((centerNode.element.properties.cy / self.map.worldMap.scale) - self.map.worldMap.transY) + 'L' +
								((value.element.properties.cx / self.map.worldMap.scale) - self.map.worldMap.transX) + ',' + 
								((value.element.properties.cy / self.map.worldMap.scale) - self.map.worldMap.transY) + 'Z';
							var color = '#666666';
							if(self.$class.lines[value.config.transportation]){
								color = self.$class.lines[value.config.transportation].color;
							}
							var newPath = centerNode.element.canvas.addPath({
								"d" : path,
								"data-code" : centerNode.config.id + '-' + value.config.id
							}, {
								initial : {
									"fill" : color,
									"fill-opacity" : 1,
									"stroke" : color,
									"stroke-opacity" : 1,
									"stroke-width" : 1 
								}
							}, g);
							
							newPath.addClass('networkPath');
							
							self.paths.push(newPath);
							
						}
					});
					
					this.map.worldMap.setSize();
					$.each(markers, function(key, value) {
						if(value == centerNode) {
							$(value.element.node).attr("r", "5");
							value.element.style.initial.r = "5";
						}
					});
				}
			}
		},
		
		createLegend : function(period){
			$('#map_legend', this.container).empty();
			if (this.data.TITLE){
				$('#map_legend', this.container).prepend('<h2>Primary Transportation Type</h2>');
			}
			$('hr', this.container).css('margin', '.5em 0');
			var legendContainer = $('#map_legend', this.container);
			if(this.data.DATA[period] != null && legendContainer) {
				var innerLegendContainer = $('<span id="network_map_markers" style="display: inline-block; margin-top: 20px; text-align:center;"></span>').appendTo(legendContainer);
				var clear = true;
				var centerLeg; 
				$.each(this.data.DATA[period], function(key, val) {
					if(val.centerNode){
						if(val.mineorplant == "mine"){
							if(val.status.toLowerCase() == "active" || val.status.toLowerCase() == "active, men working, not producing" || val.status.toLowerCase() == "active, men not working, not producing"){
								centerLeg = "A";		
							} else {
								centerLeg = "I";
							}
							switch(val.type.toLowerCase()){
								case "surface":
									centerLeg += "SUR";
									break;
								case "underground":
									centerLeg += "UND";
									break;
								default:
									centerLeg += "OTH"; 
									break;
							}
						} else {
							centerLeg = "PLNT";
						}
					}
				});
				for (var key in this.$class.lines) {
					var value = this.$class.lines[key];
					var markerContainer = $('<span class="marker_container"></span>');
					$('<span class="map_legend_marker"></span>').appendTo(markerContainer)
						.css({'background-color': value.color, 'float': 'left'})
						.after('<span>' + value.name + '</span>');
					markerContainer.appendTo(innerLegendContainer);
					if(clear){
						markerContainer.css('clear', 'left');
						clear = false;
					} else {
						clear = true;
					}
				}
				var markerContainer = $('<span class="marker_container" style="width:100%;"></span>');
				if (this.$class.rings[centerLeg].type == "ring"){
					$('<span class="legend_ring"></span>').appendTo(markerContainer)
						.css({'background-color': this.$class.rings[centerLeg].color, 'float': 'left'})
						.after('<span>' + this.$class.rings[centerLeg].name + '</span>');
					$('#map_legend', this.container).prepend($('<span class="network_node"></span>').append(markerContainer));
				} else {
					$('<span class="legend_circle"></span>').appendTo(markerContainer)
						.css({'background-color': this.$class.rings[centerLeg].color, 'float': 'left'})
						.after('<span>' + this.$class.rings[centerLeg].name + '</span>');
					$('#map_legend', this.container).prepend($('<span class="network_node"></span>').append(markerContainer));
				}
				$('#map_legend', this.container).append('<hr><div id="map_units_label_container"><span id="map_units_label">' + this.data.DATA.type_description + ' (' + this.data.DATA.units + ')</span></div>');
				$('#map_legend', this.container).append('<div id="gradientContainer"></div>');
				$('#map_legend', this.container).append('<div style="width:60%;margin-left:auto;margin-right:auto;"><span style="display:inline-block;margin-right:10px;width:20px;height:20px;background-color:#CCC"></span><span style="margin-top:3px;display:inline-block;">Value is not available</span></div>');
				this.gradientContainer = $('#gradientContainer', this.container);
				ContinuousMap.drawGradient(
					this.gradientContainer,  
					this.$class.palette[this.data.DATA.type][0], 
					this.$class.palette[this.data.DATA.type][1], 
					Highcharts.numberFormat(this.map.markerOps[1].min, this.precision), 
					Highcharts.numberFormat(this.map.markerOps[1].max, this.precision)
				);
				$('#map_legend', this.container).append('<span class="marker_disclaimer">Mine/plant sizes not to scale.</span>');
				$('#map_legend', this.container).append('<span class="marker_disclaimer">Data only includes shipments to the electric power sector.</span>');
			}
			else {
				this.options.legendTitle = null;
			}
		},

		createNotes : function(period) {
			var markers = this.unMappableMarkers[period];
			var ids = [];
			$.each(markers, function(key, val) {
				ids.push(key);
			});
			
			$('#mapNotes', this.container).empty();
			
			if(ids.length > 0) {
				$('#mapNotes', this.container).append('<span class="label">No Location Data for:</span> ');
				$.each(ids, function(index, key) {
					if(index != 0) {
						$('#mapNotes', this.container).append('; ');
					}
					$('#mapNotes', this.container).append(
						markers[key]['name'] + ' (' + markers[key]['id'] + ')'
					);
				});
			}
		},
		
		update : function(data, start, end, frequency, options, basemap) {
			var self = this;
			var newIteration = 0;
			this.displayColumns = [];
			this.counts = [];
			this.markerValues = [];
			this.unDrawNetwork();
			
			this.basemap = typeof basemap == 'undefined' ? 'us_merc_en' : basemap;
			
			this.title = data.TITLE;
			this.data = data;
			this.start = start;
			this.end = end;
			this.options = options;
			
			if(this.map) {
				this.map.stop();
				this.map.destroy();
			}
			
			this.setButtonState(false);
			
			this.map = new Map(this.container, this.basemap);
			
			this.map.colors = null;
			
			this.units = data.UNITS || '';
			this.precision = data.PRECISION;
			
			this.unMappableMarkers = {};
			
			$.each(data.DATACOLUMNS, function(i, col){
				if(col >= start && col <= end){
					self.displayColumns.push(col);
					self.counts.push(data.DATA[col]);
					self.markerValues.push(data.DATA['marker_values'][col]);
					self.unMappableMarkers[col] = {};
					$.each(data.DATA[col], function(key, val) {
						if(val['latLng'] && val['latLng'].length == 2 && (val['latLng'][0] == '' || val['latLng'][1] == '')) {
							self.unMappableMarkers[col][key] = val;
						}
					});
					$.each(self.counts[self.counts.length-1], function(key, val){
						if(val.centerNode){
							if(val.mineorplant == "mine"){
								MineListMap.styleMarkerMine(val);
							} else {
								MineListMap.styleMarkerPlant(val);
							}
						}
					});
				}

			});
			
			if(this.position !== void 0){
				newIteration = $.inArray(this.position, this.displayColumns);
				if(!~newIteration)
					newIteration = this.displayColumns.length - 1;
			}
			else{
				newIteration = this.displayColumns.length - 1;
			}
			
			this.map.animation.enabled = true;
			
			this.map.update(
				this.counts,
				this.title + ' ' + this.displayColumns[newIteration],
				$.extend(
				{
					markers : this.counts[newIteration],
					animation:{
						enabled:true,
						callback : function(iteration){
							
							self.unDrawNetwork();
							var sliderLabelCSS = {};
							var col = self.displayColumns[iteration];
							self.position = col;
							var dateLabel = (typeof col === 'undefined' || col == null) ? '' : DateCodeUtil.formatDateCode(col, frequency);
							self.map.setTitle(self.title + ' ' + dateLabel);
							self.sliderLabel.text(dateLabel);
							sliderLabelCSS.left = -0.5 * self.sliderLabel.width() + 0.5 * self.handle.width();
							if(self.sliderLabel.height() > 1.5 * parseInt(self.sliderLabel.css('font-size').slice(0, -2), 10))
								sliderLabelCSS.top = -0.5 * self.sliderLabel.height()
							else
								sliderLabelCSS.top = 0;
							self.sliderLabel.css(sliderLabelCSS);
							self.slider.slider('option', 'value', iteration);
							if(iteration == self.displayColumns.length - 1)
								self.setButtonState(false);
							self.map.worldMap.removeAllMarkers();
							self.map.worldMap.addMarkers(self.counts[iteration]);
							this.worldMap.series.markers[0].setValues(this.getCurrentCounts()[self.data.DATA.qtyType]);
							this.worldMap.series.markers[1].setValues(this.getCurrentCounts()[self.data.DATA.type]);
							self.drawNetwork(self.position);
							self.createLegend(self.position);
							self.createNotes(self.position);
						},
						iteration : newIteration
					},
					continuous : true,
					isGrowth : true,
					dataPrecision : 2,
					extremes : {
						min : -100,
						max : 100
					},
					legendItemFormatter : function(value){
						var ret;

						if(mapType == self.$class.GROWTH_MAP){
							ret = Highcharts.numberFormat(value, self.precision) + '%';
							if(value > 0)
								ret += '+'
						}
						else
							ret = Highcharts.numberFormat(value, self.precision);

						return ret;
					},
					'zoomOnScroll' : true,
					'onRegionLabelShow' : function(event, label, code){
						if (code) {
							var val = self.map.getCurrentCounts()[code];
							if(val !== void 0)
								$(label).html(
									$(label).html() + ' ' +
									Highcharts.numberFormat(val, self.precision) +
									' ' + ((self.mapType == self.$class.GROWTH_MAP) ? '% change from ' + 
									DateCodeUtil.formatDateCode(DateCodeUtil.deincrementDateCode(self.position, frequency), frequency) + ' to ' + 
									DateCodeUtil.formatDateCode(self.position, frequency) : self.units)
								);
						}
						else {
							label.empty();
						}
					},
					'onMarkerLabelShow' : function(event, label, code) {
						var value = self.map.worldMap.markers[code].config;
						var idType = "";
						if(value.mineorplant == "mine"){
							idType = "MSHA id: ";
						} else {
							idType = "Plant code: ";
						}
						if(value.centerNode == true){
							var labelTxt = 
								value['name'] + '<BR>' + 
								idType + value['id'] + '<BR>' + 
								'Location: ' + value['latLng'][0] + ', ' + value['latLng'][1];
							$(label).html(labelTxt);
						} else {
							var ranks = "";
							$.each(value['ranks'], function(key, value){
								ranks += ", " + key;
							});
							ranks = ranks.substring(2);
							var labelTxt = value['name'] + '<BR>' +
								idType + value['id'] + '<BR>' + 
								'Coal rank: ' + ranks + '<BR>' +
								'Transportation: ' + value['transportation'] + '<BR>' +
								'Location: ' + value['latLng'][0] + ', ' + value['latLng'][1] + '<BR>'; 
							if(typeof value[self.data.DATA.type] == "number"){ 
								labelTxt += self.data.DATA.type_description + ': ' + Highcharts.numberFormat(value[self.data.DATA.type], self.precision) + ' ' + self.data.DATA.units;
							} else {
								labelTxt += self.data.DATA.type_description + ': ' + value[self.data.DATA.type];
							}
							if(self.data.DATA.qtyType != self.data.DATA.type) {
								labelTxt += '<BR>Quantity: ' + Highcharts.numberFormat(value[self.data.DATA.qtyType], 0) + ' tons';
							}
							$(label).html(labelTxt);
						}
					},
					'onMarkerClick' : function(event, code) {
						if(self.map.worldMap.markers[code].config.centerNode == true){
							return false;
						}
						self.markerClicked.dispatch({'code': self.map.worldMap.markers[code].config.id}); 
					},
					'onViewportChange' : function(event, scale) {
						if(window.SVGAngle) {
							$.each(self.paths, function(index, value) {
								value.applyAttr('stroke-width', value.properties['stroke-width'] / (scale *self.map.worldMap.baseScale));
							});
						}
						if(self.map && self.map.worldMap) {
							self.focus = {
								scale : scale,
								x : -1 * ((self.map.worldMap.transX - (self.map.worldMap.width / (scale * self.map.worldMap.baseScale * 2))) / self.map.worldMap.defaultWidth),
								y : -1 * ((self.map.worldMap.transY - (self.map.worldMap.height / (scale * self.map.worldMap.baseScale * 2))) / self.map.worldMap.defaultHeight)
							};
						}
					},
					'focusOn' : self.focus
				}, options)
			);
			this.map.reset(newIteration);
			
			this.slider.slider('option', 'min', 0);
			this.slider.slider('option', 'max', this.displayColumns.length - 1);
			this.slider.slider('option', 'value', newIteration);
			
			if(typeof this.sliderHandler == 'function') {
				this.slider.unbind('slide', this.sliderHandler);
			}
			this.slider.bind('slide', this.sliderHandler);
			
			if(typeof this.playHandler == 'function') {
				this.playButton.unbind('click', this.playHandler);
			}
			this.playButton.bind('click', this.playHandler);
			
			if(typeof this.pauseHandler == 'function') {
				this.pauseButton.unbind('click', this.pauseHandler);
			}
			this.pauseButton.bind('click', this.pauseHandler);
			
			if(typeof this.resetHandler == 'function') {
				this.resetButton.unbind('click', this.resetHandler);
			}
			this.resetButton.bind('click', this.resetHandler);
		},
		
		outputToArrOfArrs : function(){
			// Need to implement
		},
		
		destroy : function() {
			this.unDrawNetwork();
			this.paths = [];
			this.$super();
		},
		
		__classvars__ : {
			palette : {
				SHIPMENT_QTY : ['#FFFFFF', Highcharts.eia_dk_blue],
				SHIPMENT_ASH : ['#FFFFFF', Highcharts.eia_grey],
				SHIPMENT_HEAT : ['#FFFFFF', Highcharts.eia_red],
				SHIPMENT_SULFUR : ['#FFFFFF', Highcharts.eia_yellow],
				SHIPMENT_PRICE : ['#FFFFFF', Highcharts.eia_green]
			},
			lines : {
				TRAIN: {name: 'Train', color: '#403203'},
				WATER: {name: 'Water', color: '#0096D7'},
				TRUCK: {name: 'Truck', color: '#FFC702'},
				OTHER: {name: 'Other', color: '#666666'}
			},
			rings : {
				'ASUR' : {name: 'Active mine - surface', color: Highcharts.eia_dk_red, type:'fill'},
				'AUND' : {name: 'Active mine - underground' , color: Highcharts.eia_dk_blue, type:'fill'},
				'AOTH' : {name: 'Active mine - other', color: Highcharts.eia_tan, type:'fill'},
				'ISUR' : {name: 'Inactive mine - surface', color:Highcharts.eia_dk_red, type:'ring'},
				'IUND' : {name: 'Inactive mine - underground' , color: Highcharts.eia_dk_blue, type:'ring'},
				'IOTH' : {name: 'Inactive mine - other', color: Highcharts.eia_tan, type:'ring'},
				'PLNT' : {name: 'Coal plant', color: '#000000', type: 'fill'}
			}
		}
	});
});
define('js/view/MediatorNetworkMap',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/MapDataProxy',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'us-merc-en',
	'coal-basin-merc-en'
], function(
	puremvc,
	Facade,
	TableDataProxy,
	MapDataProxy,
	ConfigProxy,
	ParameterProxy
) {
	return puremvc.Mediator.$extend({
		__init__ : function(viewComponent, name){
			this.$super((typeof name == 'undefined' ? this.$class.NAME : name), viewComponent); 
		},
		
		onRegister : function() {
			var self = this;
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			this.getViewComponent().markerClicked.add(function(marker, event){
				var freq = parameterProxy.getTimeParams()['freq'];
				
				var body = {
					code : marker['code'],
					freq : freq,
					type : parameterProxy.isMine() ? "plant" : "mine"
				};
				self.sendNotification(Facade.SHIPMENT_LIST_CLICK_THROUGH, body, 'ShipmentMarkerClick');
				//this.sendNotification(Facade.MINE_LIST_CLICK_THROUGH, body, 'MineMarkerClick');
			});
		},
		
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.MAP_DATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.MAP_TYPE_PARAM_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.REGION_TYPE_PARAM_UPDATED,
				Facade.UNLOAD
			]);
		},
		
		handleNotification : function(note){
			switch(note.name){
				case Facade.MAP_DATA_UPDATED:
				case Facade.MAP_TYPE_PARAM_UPDATED:
				case Facade.REGION_TYPE_PARAM_UPDATED:
				case Facade.TIME_PARAMS_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
					var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var topic = configProxy.getTopic(parameterProxy.getTopicId());
					
					if(!tableDataProxy.isUpdating && mapDataProxy.hasData){
						var self = this,
						timeParams = parameterProxy.getTimeParams(),
						data = mapDataProxy.getData();
						this.viewComponent.update(
							{
								"DATA" : data.DATA,
								"DATACOLUMNS" : data.DATACOLUMNS,
								"PRECISION" : data.PRECISION,
								"TITLE" : data.TITLE
							},
							timeParams.start,
							timeParams.end,
							timeParams.freq,
							{
								'zoomMax' : 110,
								'markerStyle' : {
									initial : { 
										cursor: 'pointer',
										fill: '#808080'
									}
								}
							},
							'us_merc_en'
						);
					}
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
                    if(note.body.target == 'map') {
                        switch(note.body.type) {
                            case 'png':
                            case 'pdf':
                                this.getViewComponent().map.exportMap(note.body.type);
                                break;
                            case 'svg':
                                if(window.SVGAngle)
                                    this.getViewComponent().map.exportMap(note.body.type);
                                else
                                    alert("Map download requires a modern browser, such as Chrome, Firefox or Internet Explorer 9+");
                                break;
                        }
                    }
					break;
				case Facade.UNLOAD:
					this.getViewComponent().destroy();
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		onRemove : function() {
			this.$super();
			this.getViewComponent().destroy();
		},
		
		__classvars : {
			NAME : 'NetworkMapMediator'
		}
	});
});

define('text!templates/mineShipmentsTableTitle.html',[],function () { return '<div>\r\n\t{{#BACKURL}}<a href="./#/{{BACKURL}}">< Return to previous page</a>{{/BACKURL}}\r\n\t<br/>\r\n\t<strong>Mine name:</strong> {{NAME}}  |  <strong>MSHA id:</strong> {{ID}} \r\n\t<br/>\r\n\t<strong>State:</strong> {{STATE}}  |  <strong>Status:</strong> {{STATUS}} \r\n\t{{#LON}} |  <a target="_blank" href="https://eia.maps.arcgis.com/apps/webappviewer/index.html?id={{WEBAPP}}&webmap={{MAP}}¢er={{LON}},{{LAT}}&level={{LEVEL}}&showLayers={{MAPLAYERS}}">View in dynamic map viewer</a>{{/LON}} \r\n\t |  Mine data\r\n</div>';});


define('text!templates/plantShipmentsTableTitle.html',[],function () { return '<div>\r\n\t{{#BACKURL}}<a href="./#/{{BACKURL}}">< Return to previous page</a>{{/BACKURL}}\r\n\t<br/>\r\n\t<strong>Plant name:</strong> {{PLANTNAME}}  |  <strong>Plant code:</strong> {{PLANTCODE}}  |  <strong>Operator:</strong> {{UTILITYNAME}}  |  <strong>Operator code:</strong> {{UTILITYCODE}}\r\n\t<br/>\r\n\t<strong>State:</strong> {{STATE}}  |  <strong>Sector:</strong> {{SECTOR}} \r\n\t{{#LON}} |  <a target="_blank" href="https://eia.maps.arcgis.com/apps/webappviewer/index.html?id={{WEBAPP}}&webmap={{MAP}}¢er={{LON}},{{LAT}}&level={{LEVEL}}&showLayers={{MAPLAYERS}}">View in dynamic map viewer</a>{{/LON}} \r\n\t |  Plant data\r\n</div>';});

define('js/pages/ShipmentNetworkPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/SiteDataConfigProxy',
	'js/model/SiteDataRequestProxy',
	'js/model/SiteDataParameterProxy',
	'js/model/MineDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/FootnoteProxy',
	'js/view/VC_PageLayout',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_TimeSelection',
	'datagrid',
	'viewStack',
	'js/view/VC_ColumnChart',
	'js/view/VC_LineChart',
	'js/view/VC_NetworkMap',
	'qTipsHelp',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorMineDataTopicSelection',
	'js/view/MediatorPlantDataTimeSelection',
	'js/view/MediatorPlantDataDataGrid',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorNetworkMap',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorPinKeyVisualization',
	'js/view/GridFormatters',
	// Templates
	'text!templates/main.html',
	'text!templates/filter_button.html',
	'text!templates/download_button.html',
	'text!templates/mineShipmentsTableTitle.html',
	'text!templates/plantShipmentsTableTitle.html',
	'text!templates/footnotes.html'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	FootnoteProxy,
	TopicPageLayout,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	TimeSelectionDialog,
	DataGrid,
	ViewStack,
	ColumnChart,
	LineChart,
	NetworkMap,
	HelpTooltipDialog,
	DownloadMediator,
	VisualizationMediator,
	TopicSelectionMediator,
	TimeSelectionMediator,
	DataGridMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	NetworkMapMediator,
	HelpTooltipMediator,
	PinKeyVisualizationMediator,
	GridFormatters,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplateMine,
	tableTitleTemplatePlant,
	footnoteTemplate
){
	var facade = null;

	//////////
	// Page //
	//////////
	
	var page = Page.$extend({
		load : function(request){
			switch(request["2"]){
				case "plant":
					request["0"] = 55;
					request["1"] = request["3"];
					break;
				case "mine":
					request["0"] = 54;
					request["1"] = request["3"];
					break;
			}
			
			$('#pagecontent').addClass('plantData');
			this.$super(request);
			Page.clearErrorMessage();
			if(!facade){
				facade = PlantDataFacade.getInstance(this.name);
				facade.sendNotification(PlantDataFacade.STARTUP, request);
			}
			else
				facade.sendNotification(PlantDataFacade.RELOAD, request);
		},
		unload : function(){
			facade.sendNotification(PlantDataFacade.UNLOAD);
			PlantDataFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('plantData');
		}
	})('js/pages/ShipmentNetworkPage');
	
	////////////
	// Facade //
	////////////
	
	var PlantDataFacade = Facade.$extend({
		
		initializeController : function(){
			this.$super();
			this.registerCommand(this.$class.STARTUP, StartupCommand);
			this.registerCommand(this.$class.RELOAD, ReloadCommand);
			this.registerCommand(this.$class.TABLE_PARAMS_UPDATED, Controller.TableParamsUpdatedCommand);
			this.registerCommand(this.$class.CHART_PARAMS_UPDATED, Controller.UpdateChartDataCommand);
			this.registerCommand(this.$class.TABLE_RENDERED, Controller.HideLoadMaskCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, TableSelectionUpdatedCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedCommand);
			this.registerCommand(this.$class.TOPIC_SELECTION_UPDATED, TopicSelectionUpdatedCommand);
			this.registerCommand(this.$class.REPORT_SELECTION_UPDATED, ReportSelectionUpdatedCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.CHART_DATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.TABLEDATA_UPDATED, ChartDataUpdatedCommand);
			this.registerCommand(this.$class.DEFAULT_CHART_SET, Controller.UpdateChartedCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.SOURCE_KEY_SELECTED, Controller.OpenAPIWindowCommand);
			this.registerCommand(this.$class.PIN_KEY_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.SHIPMENT_LIST_CLICK_THROUGH, Controller.UpdateShipmentHashCommand);
		},
		 __classvars__ : {
			
			getInstance : function(key){
				if (!puremvc.Facade.hasCore(key)) {
					new PlantDataFacade(key);
				}
				
				return puremvc.Facade.getInstance(key);
			}
			
		}
	});
	
	//////////////
	// Commands //
	//////////////
	
	var StartupCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(InitializeModelCommand);
			this.addSubCommand(InitializeViewCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var ReloadCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.UpdateRequestProxyCommand);
			this.addSubCommand(Controller.UpdateParameterProxyCommand);
		}
	});
	
	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var tableParameter = {
				'id' : 'csv_table',
				'type' : 'csv',
				'class' : 'ico_csv',
				'target' : 'table',
				'label' : 'Table (CSV)'
			};
			var downloadParams = $.extend({}, {
					'map' : (window.SVGAngle ? [
						{
							label : 'Map',
							children : [
								{
									id : 'map_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'map',
									label : 'Map (PNG)'
								},
                                {
                                    id : 'map_pdf',
                                    type : 'pdf',
                                    'class' : 'ico_pdf',
                                    target : 'map',
                                    label : 'Map (PDF)'
                                },
                                {
                                    id : 'map_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'map',
                                    label : 'Map (SVG)'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								tableParameter
							]
						}
					] : [
						{
							label : 'Data',
							children : [
								{
									id : 'map_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'map',
									label : 'Map (CSV)'
								},
								tableParameter
							]
						}
					]),
					'columnchart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'columnchart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'columnchart',
									label : 'PDF'
								},
								{
									id : 'columnchart_png',
									type : 'png',
									'class': 'ico_img',
									target : 'columnchart',
									label : 'PNG'
								},
                                {
                                    id : 'columnchart_svg',
                                    type : 'svg',
                                    'class': 'ico_img',
                                    target : 'columnchart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'columnchart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'columnchart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'columnchart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'columnchart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					],
					
					'linechart' : [
						{
							label : 'Chart',
							children : [
								{
									id : 'linechart_pdf',
									type : 'pdf',
									'class' : 'ico_pdf',
									target : 'linechart',
									label : 'PDF'
								},
								{
									id : 'linechart_png',
									type : 'png',
									'class' : 'ico_img',
									target : 'linechart',
									label : 'PNG'
								},
                                {
                                    id : 'linechart_svg',
                                    type : 'svg',
                                    'class' : 'ico_img',
                                    target : 'linechart',
                                    label : 'SVG'
                                },
                                {
                                    id : 'linechart_embed',
                                    type : 'embed',
                                    'class' : 'ico_embed',
                                    target : 'linechart',
                                    label : 'EMBED'
                                }
							]
						},
						{
							label : 'Data',
							children : [
								{
									id : 'linechart_csv',
									type : 'csv',
									'class' : 'ico_csv',
									target : 'linechart',
									label : 'Chart (CSV)'
								},
								tableParameter
							]
						}
					]
				});
			
			var requestProxy = new RequestProxy(note.getBody());
			var configProxy = new ConfigProxy();
			configProxy.setDownloadParameters(downloadParams);
			var visualizations = VisualizationDialog.defaultConfig.items.slice(); 
			//visualizations.splice(2, 1);
			configProxy.setVisualizations(visualizations);
			configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
			if(page.$class.requests.length > 1) {
				configProxy.setPreviousPage(page.$class.requests[page.$class.requests.length-2]);
			}
			
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
			var mapDataProxy = new MapDataProxy();
			var footnoteProxy = new FootnoteProxy();
			
			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(mapDataProxy);
			this.facade.registerProxy(footnoteProxy);
		}
	});
	
	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(){
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});
	
	var TableSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.UpdateHashCommand);
		}
	});
	
	var TopicSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.UpdateTopicHashCommand);
		}
	});
	
	var ReportSelectionUpdatedCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand : function(){
			this.addSubCommand(Controller.ResetStartEndCommand);
			this.addSubCommand(Controller.ResetChartedCommand);
			this.addSubCommand(Controller.ResetChartindexedCommand);
			this.addSubCommand(Controller.UpdateReportHashCommand);
		}
	});
		
	var ChartDataUpdatedCommand = puremvc.SimpleCommand.$extend({
		execute : function(note){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
			mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
			
			if(parameterProxy.getChartType() == 'map'){
				mapDataProxy.updateMapData();
			}
		}
	});
	
	/////////////
	// Proxies //
	/////////////
	var TableDataProxy = TableDataProxy.$extend({
		getMarkerData : function() {
			return this.getData()['MARKERS'];
		},
		
		loadTableData : function(){
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
			tableParamsString = parameterProxy.getTableParamsString(),
			self = this;
			
			this.isUpdating = true;
			
			if(this.inCache(tableParamsString)){
				this.setDataFromCache(tableParamsString);
				this.processChartData();
				this.isUpdating = false;
				this.sendNotification(Facade.TABLEDATA_UPDATED);
			}
			else{
				$.ajax(
					encodeURI(
						this.$class.PROXY_URL + '?method=' +
						this.$class.PROXY_METHOD + '&' + tableParamsString
					),
					{
						dataType : 'json',
						error : function(){
							self.sendNotification(Facade.ERROR);
						},
						success : function(tableData){
							// extend the table data array with our
							// getRowMetadata function
							tableData.DATA = $.extend(tableData.DATA, {
								'getRowMetadata' : self.$class.getRowMetadata
							});
							tableData.chartData = {};
							self.setData(tableParamsString, tableData);
							if(parameterProxy.isPlant()) {
								self.getInfo().ID = self.getInfo().PLANTCODE;
							}
							self.createIDMap();
							self.processLevels();
							self.processChartData('linechart');
							self.processChartData('columnchart');
							self.processChartData('map');
							self.processChartData('pin');
							self.isUpdating = false;
							self.sendNotification(Facade.TABLEDATA_UPDATED);
						}
					}
				);
			}
		},
		
		getTableTitle : function() {
			var title = this.$super();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			if(parameterProxy.isMine()) {
				title = "Shipments from " + title;
			}
			else if(parameterProxy.isPlant()) {
				title = "Shipments to " + this.getInfo()['PLANTNAME'];
			}
			return title;
		},
		
		getMappedSeries : function(seriesId, idMap) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var topic = configProxy.getTopic(parameterProxy.getTopicId());
			
			var ret = [];
			
			var seriesIdArr = seriesId.split('.');
			var geoIndex = -1;
			
			$.each(idMap, function(key, value) {
				var keyArr = key.split('.');
				if(keyArr[0] == seriesIdArr[0] && keyArr[1] == seriesIdArr[1]) {
					ret = ret.concat(value);
				}
			});
			
			return ret;
		},

		__classvars__ : {
			PROXY_METHOD : 'getAggregateShipmentData'
		}
	});

	var ParameterProxy = ParameterProxy.$extend({
		getTableParamsString : function(){
			return 'topic=' + this.getTopicId() +
				'&id=' + this.getId() +
				'&freq=' + this.getFreq() +
				'&ids=' + this.getCharted('pin');
		},
		
		isMine : function() {
			return this.facade.retrieveProxy(RequestProxy.NAME).getTopicId() == 54;
		},
		
		isPlant : function() {
			return this.facade.retrieveProxy(RequestProxy.NAME).getTopicId() == 55;
		}
	});
	
	var RequestProxy = RequestProxy.$extend({
		getPath : function(){
			var pattern = this.getData()['request_'], i;
			
			var firstPart = pattern.substr(0, pattern.indexOf('/')+1);
			pattern = pattern.substr(firstPart.length, pattern.length);
			
			if(pattern.indexOf('/') != -1) {
				if(pattern.indexOf('?') != -1) {
					i = Math.min(pattern.indexOf('/'), pattern.indexOf('?'));
				}
				else {
					i = pattern.indexOf('/');
				}
			}
			else
			{
				if(pattern.indexOf('?') != -1) {
					i = pattern.indexOf('?');
				}
				else {
					i = pattern.length;
				}
			}
			return firstPart + pattern.substring(0, i);
		}
	});
	
	var MapDataProxy = puremvc.Proxy.$extend({
		__init__ : function(){
			this.$super(this.$class.NAME);
		},
		
		updateMapData : function(){
			var data = {};
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			
			var tableData = tableDataProxy.getTableData();
			var tableInfo = tableDataProxy.getInfo();
			var columns = tableDataProxy.getDataColumns();
			
			var chartSelection = parameterProxy.getCharted(parameterProxy.getChartType());
			var selection_dataType = chartSelection.split('.')[1];
			var quantity_dataType = 'SHIPMENT_QTY';
			
			data["marker_values"] = {};
			for(var i = 0; i < columns.length; i++) {
				data[columns[i]] = [];
				data["marker_values"][columns[i]] = {};
				data["marker_values"][columns[i]][selection_dataType] = [];
				data["marker_values"][columns[i]][quantity_dataType] = [];
			}
			var markerData = tableDataProxy.getMarkerData();
			var precision = '';
			
			data["type"] = typeof selection_dataType == 'undefined' ? quantity_dataType : selection_dataType;
			data["qtyType"] = quantity_dataType;
			// Pivot table data for use by the map view components.
			for(var i = 0; i < tableData.length; i++) {
				if(tableData[i].HAS_DATA) {
					var currentRow_dataType = tableData[i].SERIES_ID.split('.')[1];
					if(currentRow_dataType == selection_dataType || currentRow_dataType == quantity_dataType) {
						var code = tableData[i].ID;
						var rank = tableData[i].COAL_RANK;
						
						if(currentRow_dataType == selection_dataType) {
							
							data["type_description"] = tableData[i].DESCRIPTION;
							data["units"] = tableData[i].UNITS;
							precision = tableData[i].PRECISION;
						}
						
						$.each(tableData[i].DATA, function(key, value) {
							if(value != "--") {
								if(data[key].length == 0 || data[key][data[key].length-1]['id'] != code) {
									var elem = $.extend(
										{
											ranks : {},
											mineorplant : parameterProxy.isMine() ? 'plant' : 'mine'
										}, 
										markerData[code]
									);
									
									elem['ranks'][rank] = null;
									elem['transportation'] = markerData[code]['transportation'][key];
									data[key].push(elem);
								}
								
								data[key][data[key].length-1][currentRow_dataType] = value;
							}
						});
					}
				}
			}
			$.each(columns, function(index, period) {
				
				data[period].push({
					id : tableInfo.ID,
					latLng : [tableInfo.LAT, tableInfo.LON],
					name :  parameterProxy.isMine() ? tableInfo.NAME : tableInfo.PLANTNAME,
					centerNode : true,
					transportation : null,
					type : tableInfo.TYPE,
					status : tableInfo.STATUS,
					mineorplant : parameterProxy.isMine() ? 'mine' : 'plant'
				});
			});
			
			// Sort so the markers are rendered with the largest being under the smallest.
			for(var i = 0; i < columns.length; i++) {
				data[columns[i]].sort(function(a, b) {
					return b[quantity_dataType] - a[quantity_dataType];
				});
				$.each(data[columns[i]], function(index, value) {
					if(typeof value[quantity_dataType] != 'undefined' && value.latLng.join() != ",") {
						data['marker_values'][columns[i]][quantity_dataType][index] = value[quantity_dataType];
					}
					
					if(typeof value[quantity_dataType] != 'undefined' && value.latLng.join() != ",") {
						data['marker_values'][columns[i]][selection_dataType][index] = value[selection_dataType];
					}
				});
			}
			
			this.setData({"DATACOLUMNS": columns, "DATA": data, "TITLE": tableDataProxy.getTableTitle(), "PRECISION" : precision});
			this.hasData = true;
			this.sendNotification(Facade.MAP_DATA_UPDATED);
		},
		
		__classvars__ : {		
			NAME : 'MapDataProxy'
		}
	});
		
	///////////////
	// Mediators //
	///////////////
	
	var PageMediator = puremvc.Mediator.$extend({
		
		__init__ : function(viewComponent){
			this.$super(this.$class.NAME, viewComponent);
		},
		
		onRegister : function(){
			var pageLayout = this.getViewComponent();
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
			
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), {items : configProxy.getVisualizations()});
			var topTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getTopTopicSelectionContainer(), true);
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var timeSelectionDialog = new TimeSelectionDialog(pageLayout.getTimeSelectionContainer());
			var dataGrid = new DataGrid(pageLayout.getTableContainer());
			var viewStack = new ViewStack(pageLayout.getChartContainer());
			var pinKeyVisualizationDialog = new VisualizationDialog(pageLayout.getPinKeyVisualizationContainer(), {items : configProxy.getLinkTypes()});

 			
			var lineChartContainer = viewStack.addView('linechart');
			var columnChartContainer = viewStack.addView('columnchart');
			var mapContainer = viewStack.addView('map');
			
			var columnChart = new ColumnChart(columnChartContainer);
			var lineChart = new LineChart(lineChartContainer);
			var map = new NetworkMap(mapContainer);
			var helpTooltipDialog = new HelpTooltipDialog();

			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var middleTopicSelectionMediator = new TopicSelectionMediator(topTopicSelectionDialog, true);
			var topTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var timeSelectionMediator = new TimeSelectionMediator(timeSelectionDialog);
			var dataGridMediator = new DataGridMediator(dataGrid);
			var viewStackMediator = new ViewStackMediator(viewStack);
			var lineChartMediator = new LineChartMediator(lineChart);
			var columnChartMediator = new ColumnChartMediator(columnChart);
			var mapMediator = new NetworkMapMediator(map);
			var helpTooltipMediator = new HelpTooltipMediator(helpTooltipDialog);
			var pinKeyVisualizationMediator = new PinKeyVisualizationMediator(pinKeyVisualizationDialog);

			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(topTopicSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(timeSelectionMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(mapMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);

			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
			footnoteProxy.setData({
				rseAvailable : false,
				showFilterValues : true,
				showDetail : true,
				showTransportationMode : true,
				showPrimeMovers : parameterProxy.isPlant(),
				showPlantFuelTypes : parameterProxy.isPlant(),
				showMineType : parameterProxy.isMine(),
				showMineStatus : parameterProxy.isMine()
			});
			
			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, {
					urlArgs : footnoteProxy.getUrlArgs(), 
					footnoteText : footnoteProxy.getFootnotes()
				}));
				
			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
			
			
		},
		
		listNotificationInterests : function(){
			return this.$super().concat([
				Facade.RELOAD,
				Facade.TABLEDATA_UPDATED,
				Facade.TABLE_RENDERED
			]);
		},
		
		handleNotification : function(note){
			var vc = this.getViewComponent();
			switch(note.getName()){
				case Facade.RELOAD:
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var footnoteProxy = this.facade.retrieveProxy(FootnoteProxy.NAME);
					footnoteProxy.setData({
						rseAvailable : false,
						showFilterValues : true,
						showDetail : true,
						showTransportationMode : true,
						showPrimeMovers : parameterProxy.isPlant(),
						showPlantFuelTypes : parameterProxy.isPlant(),
						showMineType : parameterProxy.isMine(),
						showMineStatus : parameterProxy.isMine()
					});
					
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, {
							urlArgs : footnoteProxy.getUrlArgs(), 
							footnoteText : footnoteProxy.getFootnotes()
						}));
						
					$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({'autoDimensions':false, 'autoScale':false,'width':900, 'height': 600,'scrolling':'auto'});
					break;
				case Facade.TABLEDATA_UPDATED:
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
					var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					var prevPage = configProxy.getPreviousPage();
					
					vc.getTitleContainer().empty();
					vc.getTitleContainer().css('display', 'block');
					var template = parameterProxy.isMine() ? tableTitleTemplateMine : tableTitleTemplatePlant;
					
					this.getMapUrl(vc, tableDataProxy, prevPage, parameterProxy, template);
					

					window.document.title = 'Coal Data Browser - ' + tableDataProxy.getTableTitle();

					break;
				case Facade.TABLE_RENDERED:
					if(vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},

		getMapUrl: function(vc, tableDataProxy, prevPage, parameterProxy, template){
			//perform ajax call to layer-config.php script.
			let mapId = 'coal-map';
			let url = window.location.protocol + '//' + window.location.hostname + '/global/scripts/maps/layer-config.php';
			let layers = '';
			let zoomLevel, webapp, webmap = null;
			$.ajax(
				encodeURI(url + '?mapId=' + mapId + '&level=1;'	
				),
				{
					dataType : 'json',
					error : function(){
						self.sendNotification(Facade.ERROR);
					},
					success : function(data, textStatus, jqXHR) {
						var filteredArray = data.mapId.filter(function(e) { return e !== 'State Mask' })
						for(let layer of filteredArray){
							console.log(layer);
							layers += layer + ';';
						}
						zoomLevel = data.level;
						webapp = data.webapp;
						webmap = data.webmap;
						console.log(webapp);
						console.log(webmap);
						console.log(zoomLevel);
					var linkArgs = parameterProxy.getQueryString({'ctype':''});
					var tableInfo = $.extend(tableDataProxy.getInfo(), {BACKURL:(prevPage ? prevPage.request_ : parameterProxy.getFullTopicPath({}, parameterProxy.getTopicId())), LINK_ARGS : linkArgs,  LEVEL: zoomLevel, MAPLAYERS : layers, WEBAPP : webapp, MAP: webmap});
					vc.getTitleContainer().append(Mustache.to_html(template, tableInfo));	
					

						
					}
				}
			);
			//pass in the layers to load


		},
		markerClickHandler : function(marker, event) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var freq = parameterProxy.getTimeParams()['freq'];
			var type;
			
			if(parameterProxy.isMine()) {
				type = 'mine';
			}
			else {
				type = 'plant';
			}
			
			var label = 'shpiments/' + type + "/" + marker['code'] + '?freq=' + freq;
					
			var body = {
				label : label,
				code : marker['code'],
				freq : freq,
				type : type
			};

			this.sendNotification(Facade.SHIPMENT_LIST_CLICK_THROUGH, body, 'ShipmentListClickThrough');
		
		},
		
		__classvars__ : {
			NAME : 'PageMediator'
		}
		
	});
	
	var DataGridMediator = DataGridMediator.$extend({
		generateColumns : function(dataColumns, data, freq) {
			var columns = this.$super(dataColumns, data, freq);
			
			for(var i = 0; i < columns.length; i++) {
				if(columns[i].id == 'description') {
					columns[i].displayFormatter = function(row, cell, value, columnDef, datum) {
						var description = GridFormatters.descriptionFormatter(row, cell, value, columnDef, datum);
						var descriptionObj = $('<div>' + description + '</div>');
						if(datum.ID != null && !datum.HAS_DATA && datum.ID != '') {
							$('.description_text_wrapper', descriptionObj).append(" <span class=\"shipment_link_cell\">[" + datum.ID + "]</span>");
						}
						return descriptionObj.html();
					};
					break;
				}
			}
			
			return columns;
		},
		
		
		dataGridClick : function(row, cell, datum, e){
			var vc = this.getViewComponent(), columns = vc.columns;
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			
			if(columns[cell].id == 'description' && $(e.target).hasClass('shipment_link_cell')) {
				if(datum.ID != null && !datum.HAS_DATA) {
					var freq = parameterProxy.getTimeParams()['freq'];
				
					var body = {
						code : datum.ID,
						freq : freq,
						type : parameterProxy.isMine() ? "plant" : "mine"
					};
					this.sendNotification(Facade.SHIPMENT_LIST_CLICK_THROUGH, body, 'ShipmentTableClick');
				}
			}
			else {
				return this.$super(row, cell, datum);
			}
		}
	});
	/////////////////////
	// View Components //
	/////////////////////
	
	var PageLayout = TopicPageLayout.$extend({
		
		__init__ : function(){
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts' : {
					'disabled' : true,
					'selected' : false,
					'id' : 'main_filter_button'
				},
				'overlayFilterButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_filter_button'
				},
				'mainVisualizationButtonOpts' : {
					'selected' : false,
					'id' : 'main_visualization_button'
				},
				'overlayVisualizationButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_visualization_button'
				},
				'mainDownloadButtonOpts' : {
					'selected' : false,
					'id' : 'main_download_button'
				},
				'overlayDownloadButtonOpts' : {
					'selected' : true,
					'id' : 'overlay_download_button'
				}
			});
		}
	});
	
	return page;
});
define('js/routes',[
	'js/pages/CoalAggregateDataPage',
	'js/pages/ImportExportPage',
	'js/pages/MineListPage',
	'js/pages/MineDataPage',
	'js/pages/AggregateShipmentPage',
	'js/pages/ShipmentListPage',
	'js/pages/ShipmentNetworkPage'
], {
	'defaultHash' : 'topic/33',
	'patterns' :[
		{
			'pattern' : /^topic\/(\d{1,2})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/CoalAggregateDataPage',
			'rules' : {
				0 : [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37]
			}
		}, {
			'pattern' : /^topic\/(\d{1,2})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/AggregateShipmentPage',
			'rules' : {
				0 : [44,45,46,47,48,49,50,51,52,53]
			}
		}, {
			'pattern' : /^topic\/(\d{1,2})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/ImportExportPage',
			'rules' : {
				0 : [39,40,41,42]
			}
		}, {
			'pattern' : /^topic\/(\d{1,2})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/MineListPage',
			'rules' : {
				0 : [38]
			}
		}, {
			'pattern' : /^mine\/()(\d{1,7})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/MineDataPage'
		}, {
			'pattern' : /^topic\/(\d{1,2})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/ShipmentListPage',
			'rules' : {
				0 : [54,55]
			}
		},{
			'pattern' : /^shipments\/()()(mine|plant)\/(\d{1,7})\/?(\?.*)?/,
			'moduleURI' : 'js/pages/ShipmentNetworkPage'
		}
	]
});
define(
	'js/router',[
		'js/routes',
		'crossroads',
		'hasher',
		'page'
	],
	function(
		routes,
		crossroads,
		hasher,
		Page
	){
		///////////////////////
		// Private Variables //
		///////////////////////
		
		/**
		 * The currently loaded page module
		 **/
		var currentPageModule = null,
		/**
		 * A hash to redirect to in the case of an unsuccesful routing
		 **/
		redirectHash;
		
		/////////////////////
		// Private Methods //
		/////////////////////
		
		/**
		 * Shim for Array.indexOf in IE 7
		 **/
		function arrayIndexOf(arr, val) {
			if (arr.indexOf) {
				return arr.indexOf(val);
			} else {
				var n = arr.length;
				while (n--) {
					if (arr[n] === val) {
						return n;
					}
				}
				return -1;
			}
		}
		
		/**
		 * Parses the query strings into objects
		 **/
		function decodeQuery(queryString){
			var queryArr = (queryString || '').replace('?', '').split('&'),
				n = queryArr.length,
				queryObj = {},
				value, key;
			while (n--) {
				queryArr[n] = queryArr[n].split('=');
				value = queryArr[n][1];
				//parseFloat("") -> NaN
				queryObj[queryArr[n][0]] = value;//(value === '' || isNaN(value))? value : parseFloat(value);
			}
			
			return queryObj;
		}
		
		/**
		 * Checks for query strings and parses them into vals.query
		 **/
		function normalizeFn(req, vals) {
			var pattern = this,
			tmp = {
				'query' : {}
			}, key;
			
			function processDefaultValue(def, key){
				if(def){
					if(typeof def === 'object'){
						var tmpQuery = {};
						$.extend(tmpQuery, def, tmp['query']);
						tmp['query'] = tmpQuery;
					}
					else if(typeof def == 'function'){
						processDefaultValue(def.call(pattern, req, tmp), key)
					}
					else if(tmp[key] == void 0){
						tmp[key] = def;
					}
				}
			}
			
			// Rirst, loop through and process the existing values
			for(key in vals){
				if(vals.hasOwnProperty(key)){
					if(vals[key] !== void 0){
						tmp[key] = vals[key];
						if(key == 'query' ||
							(
								typeof vals[key] == 'string' &&
								vals[key].indexOf('?') == 0
							)
						)
							// decode query string values, with the defaults in the pattern
							$.extend(tmp['query'], decodeQuery(vals[key]));
					}
				}
			}
			// Now loop through and process the default values
			if(pattern.defaults){
				for(key in vals){
					if(vals.hasOwnProperty(key)){
						processDefaultValue(pattern.defaults[key], key)
					}
				}
			}
			return [tmp];
		};
		
		/**
		 * Wraps crossroads parameter validation rules in a function that
		 * sets a redirect hash in the event that a hash fails to match a
		 * pattern.
		 **/
		function wrapRules(pattern){
			var rules, key, oldRule;
			if(rules = pattern.rules){
				for(key in rules){
					if(rules.hasOwnProperty(key) && key != 'normalize_'){
						oldRule = rules[key];
						rules[key] = function(oldRule){
							return function(val, request, values){
								var isValid = true;
								
								if (oldRule instanceof RegExp) {
									isValid = oldRule.test(val);
								}
								else if (oldRule instanceof Array) {
									isValid = arrayIndexOf(oldRule, val) !== -1;
								}
								else if (oldRule instanceof Function) {
									isValid = oldRule(val, request, values);
								}
								
								if(!isValid && pattern.defaultHash)
									redirectHash = pattern.defaultHash;
								
								return isValid;
							}
						}(oldRule);
					}
				}
			}
			else{
				rules = pattern.rules = {};
			}
			
			rules['normalize_'] = function(req, vals){
				return normalizeFn.call(pattern, req, vals);
			};
			
			return pattern;
		}
		
		/**
		 * This method runs when a route is successful
		 **/
		function routed(request, data){
			if(typeof console !== 'undefined' && typeof console.log == 'function')
				console.log(request);
		}
		
		/**
		 * This method runs when a route is bypassed
		 **/
		function bypassed(request){
			var r;
			// If we have a redirect hash
			if(redirectHash){
				// Prevent an infinite loop
				if(redirectHash != request){
					r = redirectHash;
					redirectHash = null;
					// Don't redirect the hash, just parse the path
					crossroads.parse(r);
				}else{
					throw 'Bad redirect hash: ' + redirectHash;
				}
			}
			else{
				crossroads.parse(routes.defaultHash);
			}
		}
		
		/**
		 * Method for loading page modules on successful routes
		 **/
		function loadPage(){
			var routeArgs = arguments;
			try{
				require([
					this.moduleURI // Load the page module
				], function(module){
					var success;
					if(currentPageModule != module) unloadCurrentPage();
					
					try{
						module.load.apply(module, routeArgs);
					}catch(e){
						//if(false) {   //for testing error handler, under vm-server
						if(location.host.indexOf("wwwdev") != -1  || location.host.indexOf("vdc") != -1) {
							throw e;
						}
						else {
							var jsonData = JSON.stringify(e);
							var module = module.toString();
							var route = JSON.stringify(routeArgs[0]);
							var error = e.name + ' ' + e.message;
							var stack = e.stack;
							var logStr = '&route='+route+'&module='+module+'&error='+error+'&stack='+stack;

							Page.errorHandler({
								'module' : module.toString(),
								'route' : routeArgs[0],
								'error' : e.name + ' ' + e.message,
								'stack' : e.stack
							}, 'data/errorService.php?method=logError'+logStr);
						}
					}
					
					currentPageModule = module;
				});
			}catch(e){
				//if(false) {   //for testing error handler, under vm-server
				if(location.host.indexOf("wwwdev") != -1  || location.host.indexOf("vdc") != -1) {
					throw e;
				}
				else {
					var jsonData = JSON.stringify(e);
					var module = this.moduleURI;
					var route = JSON.stringify(routeArgs[0]);
					var error = e.name + ' ' + e.message;
					var stack = e.stack;
					var logStr = '&route='+route+'&module='+module+'&error='+error+'&stack='+stack;

					Page.errorHandler({
						'module' : this.moduleURI,
						'route' : routeArgs[0],
						'error' : e.name + ' ' + e.message,
						'stack' : e.stack
					}, 'data/errorService.php?method=logError'+logStr);
				}
			}
		};
		
		/**
		 * Calls the unload method on the currently loaded page module
		 **/
		function unloadCurrentPage(){
			if(currentPageModule){
				currentPageModule.unload();
				currentPageModule = null;
			}
		}
		
		/**
		 * Adds page routes and associates them with their page module URI
		 **/
		function addRoute(pattern, moduleURI, rules){
			var route = crossroads.addRoute(pattern);
			
			if(rules) route.rules = rules;
			
			route.matched.add(loadPage, {
				'moduleURI' : moduleURI
			});
		}
		
		////////////////////
		// Public Methods //
		////////////////////
		
		/**
		 * The initialization function for the router module.  Sets up
		 * crossroads and initalizes hasher.
		 **/
		function init(){
			var i, pattern;
			// Loop through the patterns in the routes module and add them
			for(i=0; i<routes.patterns.length; i++){
				pattern = wrapRules(routes.patterns[i]);
				addRoute(pattern.pattern, pattern.moduleURI, pattern.rules);
			}
			// On a successful routing
			crossroads.routed.add(routed);
			// On a bypassed routing
			crossroads.bypassed.add(bypassed);
			// Typecast route tokens
			crossroads.shouldTypecast = true;
			// Add crossroads.parse as a listener for hasher's initalized
			// and changed signals
			hasher.initialized.add(crossroads.parse, crossroads);
			hasher.changed.add(crossroads.parse, crossroads);
			// Init hasher
			hasher.init();
			// If we don't have a hash, set it to the initalHash
			if(hasher.getHash() == '') crossroads.parse(routes.defaultHash);
		}
		
		///////////////////////
		// Module definition //
		///////////////////////
		
		return {
			'init' : init
		};
	}
);
require([
	'js/router',
	'page'
],
	function(router, Page){
		Page.showLoadMask();
		$(router.init);
	}
);
define("js/main", function(){});