/**
 *	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',

			TAB_PARAMS_UPDATED : 'tabParameterUpdated',

			FREQUENCY_PARAM_UPDATED : 'frequencyParameterUpdated', 
			
			COMPARE_CHART_DATA_UPDATED : 'compareChartDataUpdated',

			// 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',

			TAB_SELECTION_UPDATED: 'tabSelectionUpdated',

			// Map Click

			STATE_MAP_CLICKED: 'stateMapClicked',

			// Page click through

			AGGREGATE_DATA_CLICK_THROUGH: 'aggregateDataClickThrough',

			PLANT_LIST_CLICK_THROUGH: 'plantListClickThrough',

			POWER_PLANT_HIGHLIGHT: 'powerPlantHighlight',

			// View state changes

			TABLE_RENDERED: 'tableRendered',

			UPDATE_VISUALIZATION_VIEW_STACK: 'updateVisualizationViewStack',

			SOURCE_KEY_SELECTED: 'sourceKeySelected',

			REMOVE_PLANT_COMPARE : 'removePlant',

			SHOW_COMPARE_CHART : 'showCompareChart',

            // Plant Compare Selection updates/click notifications

            PLANT_COMPARE_TOGGLE_CLICKED: 'plantCompareToggleClicked',

            PLANT_COMPARE_TOGGLE_PARAM_UPDATED: 'plantCompareToggleParamUpdated',

            PLANT_COMPARE_SELECTION_CLICKED: 'plantCompareSelectionClicked',

            PLANT_COMPARE_SELECTION_UPDATED: 'plantCompareSelectionUpdated',

            PLANT_COMPARE_SUBMIT_CLICKED: 'plantCompareSubmitClicked',

            // Auto Redirect

            AUTO_REDIRECT: 'autoRedirect',

			// 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;
				ret[aggColumns[i].filterGroup.varName] = '';

				for (j = 0; j < filterValues.length; j++) {
					if (filterValues[j].filterValue == seriesIdSplit[i]) {
						valIndex = filterValues[j].valueId;
						if(filterValues[j].filterQuery !== null) {
							ret[aggColumns[i].filterGroup.varName] = filterValues[j].filterQuery;
						}
						break;
					}
				}

				if(ret[aggColumns[i].filterGroup.varName] === "") {
					checkedArray = new Array(1);
					checkedArray[valIndex] = 1;

					var binaryString = "";
					for (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());
			console.log(requestProxy.getData());
		}
	});

	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.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);
				}
			}
            var breakExp = /<BR\s*\/?>/i;
            input1 = $('<input type="hidden" name="csvArr"/>').val(JSON.stringify(note.getBody()).replace(new RegExp(breakExp, 'g'), ' '));
            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);
		}
	});

	var FrequencySelectionUpdatedSingleTimeSliderCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand: function() {
            this.addSubCommand(UpdateDateRangeSelectionCommand);
			this.addSubCommand(UpdateHashCommand);
		}
	});

    var UpdateDateRangeSelectionCommand = puremvc.SimpleCommand.$extend({
        execute: function(note) {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            note.getBody().datecode = parameterProxy.getInitialDateCode(note.getBody().freq);
        }
    });

	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,
		'FrequencySelectionUpdatedCommand': FrequencySelectionUpdatedCommand,
		'FrequencySelectionUpdatedSingleTimeSliderCommand': FrequencySelectionUpdatedSingleTimeSliderCommand,
        'UpdateDateRangeSelectionCommand': UpdateDateRangeSelectionCommand
	};
});

define('js/model/mixins/ConfigMixinVisualization',[], function() {
	return {
		getVisualizations: function() {
			return this.getData().visualizations;
		},

		setVisualizations: function(visualizations) {
			this.getData().visualizations = visualizations;
		}
	};
});

define('js/model/mixins/ConfigMixinDownload',[], function() {
	return {
		getDownloadParameters: function() {
			return this.getData().downloadParameters;
		},

		setDownloadParameters: function(parameters) {
			this.getData().downloadParameters = parameters;
		}
	};
});

define('js/model/mixins/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) {
			this.getData().currentView = ($.inArray(view, this.getViews()) == -1 ? this.getViews()[0] : view);
			
			return this.getData().currentView;
		}
	};
});

define('js/model/mixins/ConfigMixinLinkType',[], function() {
	return {
		getLinkTypes: function() {
			return this.getData().linkTypes;
		},

		setLinkTypes: function(linkTypes) {
			this.getData().linkTypes = linkTypes;
		}
	};
});

define('js/model/aggregate/AggregateDataConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/mixins/ConfigMixinVisualization',
	'js/model/mixins/ConfigMixinDownload',
	'js/model/mixins/ConfigMixinViewStack',
	'js/model/mixins/ConfigMixinLinkType'
], function(
	ConfigProxy,
	ConfigMixinVisualization,
	ConfigMixinDownload,
	ConfigMixinViewStack,
	ConfigMixinLinkType
) {
	return ConfigProxy.$extend({
		__include__: [
			ConfigMixinVisualization,
			ConfigMixinDownload,
			ConfigMixinViewStack,
			ConfigMixinLinkType
		]
	});
});

define('js/model/mixins/RequestMixinTopic',[], function() {
	return {
		getTopicId: function() {
			return this.getData()['0'];
		}
	};
});

define('js/model/aggregate/AggregateDataRequestProxy',[
	'js/model/RequestProxy',
	'js/model/mixins/RequestMixinTopic'
], function(
	RequestProxy,
	RequestMixinTopic
) {
	return RequestProxy.$extend({
		__include__: [
			RequestMixinTopic
		]
	});
});

define('js/model/mixins/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/mixins/ParameterMixinGetCharted',[], function() {
	return $.extend({
		getCharted: function(type) {
			return this.getQuery()[type];
		}
	});
});

define('js/model/mixins/ParameterMixinChart',[
	'js/model/mixins/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/mixins/ParameterMixinFrequency',[], function() {
	return {
		getFreq: function() {
			return this.getQuery().freq;
		},

		getFreqTitle: function() {
			var ret = '';

			switch (this.getFreq()) {
				case 'A':
					return 'Annual';
				case 'Q':
					return 'Quarterly';
				case 'M':
					return 'Monthly';
			}

			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;
					case 'Q':
						return topic.hasQuarterly;
					case 'M':
						return topic.hasMonthly;
				}
				return false;
			});
		},

        enableFrequency: function() {
            return true;
        }
	};
});

define('js/model/mixins/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/mixins/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/mixins/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/mixins/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/mixins/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/aggregate/AggregateDataParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/RequestProxy',
	'js/model/mixins/ParameterMixinAggColumn',
	'js/model/mixins/ParameterMixinChart',
	'js/model/mixins/ParameterMixinFrequency',
	'js/model/mixins/ParameterMixinLinkType',
	'js/model/mixins/ParameterMixinReport',
	'js/model/mixins/ParameterMixinRSE',
	'js/model/mixins/ParameterMixinStartEnd',
	'js/model/mixins/ParameterMixinTopic'
], function(
	Facade,
	ConfigProxy,
	ParameterProxy,
	RequestProxy,
	ParameterMixinAggColumn,
	ParameterMixinChart,
	ParameterMixinFrequency,
	ParameterMixinLinkType,
	ParameterMixinReport,
	ParameterMixinRSE,
	ParameterMixinStartEnd,
	ParameterMixinTopic
) {
	return ParameterProxy.$extend({

		__include__: [
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinAggColumn,
			ParameterMixinStartEnd,
			ParameterMixinChart,
			ParameterMixinRSE,
			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 ret.concat([
				'freq',
				'start',
				'end',
				'chartindexed',
				'ctype',
				'ltype',
				'columnendpoints',
				'columnvalues',
				'tab'
			]);
		},

		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 += '&tab=' + 'this';

			return ret;
		},

		getTableParams: function() {
			return $.extend({
				'topic': this.getTopicId(),
				'freq': this.getFreq(),
				'agg': this.getAgg(),
				'ids': this.getCharted('pin')
			}, 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,
				frequencyParamUpdated = false,
				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 |= frequencyParamUpdated |= 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 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(frequencyParamUpdated)
				this.sendNotification(Facade.FREQUENCY_PARAM_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);
		},

		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());
		}

	});
});

define('js/model/mixins/TableDataMixinAnalysis',[], function() {
	return {
		getRelatedAnalysis: function() {
			return this.getData().ANALYSIS.ARTICLES;
		},
		getMoreAnalysisURL: function() {
			return this.getData().ANALYSIS.MORE_URL;
		}
	};
});

define('js/model/mixins/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;
			var datum;
			if (this.inCache(tableParamsString)) {
				this.setDataFromCache(tableParamsString);
			} else {
				for (i = 0; i < seriesData.length; i++) {
					datum = seriesData[i];
					seriesMap[datum.SERIES_ID] = true;
				}

				for (i = 0; i < pinnedData.length; i++) {
					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++) {
					datum = seriesData[i];
					if (!(datum.SERIES_ID in pinnedMap)) {
						seriesData.splice(i, 1);
					}
				}

				this.setData(tableParamsString, $.extend({}, this.getData(), {
					SERIESDATA: {
						DATA: seriesData
					}
				}));
			}

			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);
							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 indices;
			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, topic, query, defaults, matchesDefault;

            function setChartType(index, value) {
                var datum = combinedData[value];
                datum[chartType] = true;
            }

            function checkIfMatchesDefault(key, value) {
                if (query[key] != value) {
                    matchesDefault = false;
                }
            }

			var chartTypes = chartType !== void 0 ? [chartType] :
				$.map(configProxy.getVisualizations(), function(vis) {
					return vis["class"];
				});

			for (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;

						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') {
						topic = configProxy.getTopic(parameterProxy.getTopicId());
						query = parameterProxy.getQuery();
						defaults = $.parseJSON(topic.defaultSelection);
						matchesDefault = true;

						$.each(defaults.query, checkIfMatchesDefault);

						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) {
							indices = this.getMappedSeries(chartedIds[0], idMap);
							$.each(indices, setChartType);
						}
					}
					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)) {
						indices = [];

						if (chartType == 'map') {
							indices = this.getMappedSeries(datum.SERIES_ID, idMap);
						} else {
							indices = idMap[datum.SERIES_ID];
						}

						for (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]);
							}
						}
					}
				}

                var combinedTypeData = this.getCombinedTableDataByType();
				for (i = 0; i < combinedTypeData.length; i++) {
					datum = combinedTypeData[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 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 == 'geo') {
					filterValArr[index] = '[A-Za-z]+';
					return false;
				}
			});

			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/model/mixins/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/aggregate/AggregateDataTableDataProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/model/mixins/TableDataMixinAnalysis',
	'js/model/mixins/TableDataMixinChart',
	'js/model/mixins/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;
		},

        getCombinedTableDataByType: function() {
            return this.getCombinedData();
        },

		getTableUnits: function() {
			return this.getData().TABLEDATA.UNITS === '' ? null : this.getData().TABLEDATA.UNITS;
		},

		getTableTitle: function() {
			return this.getData().TABLEDATA.DESCRIPTION;
		},

        getVisualizationTitle: function() {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            var chartType = parameterProxy.getChartType();
            var arePinnedSeriesCharted;
            if (chartType == 'linechart' || chartType == 'columnchart' || chartType == 'pin' || chartType == 'sourcekey')
                arePinnedSeriesCharted = parameterProxy.arePinnedSeriesCharted(chartType);
            return (arePinnedSeriesCharted ? 'Custom Chart' : this.getTableTitle()) + ', ' + parameterProxy.getFreqTitle();
        },

		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'
		}
	});
});

/*
 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/HelpToolTipsEDB',['datagrid'], function(DataGrid) {
	return function() {
		return [
			//Help button
			{ selector: '#help_button', tip: '<iframe width="230" height="200" src="http://www.youtube.com/embed/gQ70w2KDbPI?rel=0" 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>Electricty Data Browser</b>.  Choose a report, which correspond to the chapters in the Electric Power Monthly, to automatically select a dataset and configure the filter.  Alternatively, you can select a dataset directly but then you must configure the filter.  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>Electricty Data Browser</b>.  Choose a report, which correspond to the chapters in the Electric Power Monthly, to automatically select a dataset and configure the filter.  Alternatively, you can select a dataset directly but then you must configure the filter.  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 electricity 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' },

			//RSE checkbox
			{ selector: '#rse_checkbox', tip: 'This checkbox enables the display of Relative Standard Error (RSE) values. These are shown as a percent value in parentheses next to the data values in the table above.', position: 'rightMiddle' },

			//Terms and Definitions
			{ selector: '#termsLink', tip: 'Click this link to see any notes and definitions of terms.', position: 'topMiddle' },

			//Map Controls
			{ selector: '#map #controls_container', tip: 'The map controls allow you to switch between coloring the states based on the current period value or the percent change from the previous period. You can also play the map over time or scroll to a specific 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, individual data points will be hyperlinks for drilling down to the electric 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 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 drill down to the electric 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' }
		];
	};
});

define('js/model/HelpTooltipProxy',[
		'puremvc',
		'js/view/HelpToolTipsEDB'
	],
	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) {
								var count;
								var state;
								title = data.TITLE;
								for (var key in data.DATA) {
									if (data.DATA.hasOwnProperty(key)) {
										count = data.DATA[key];
										for (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('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/MediatorElectricityTopicSelection',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/view/MediatorTopicSelection'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	MediatorTopicSelection
) {
	return MediatorTopicSelection.$extend({


		arrangeTopics: function(topics, selectedTopic) {
			var topicGroups = [
				{ label: 'Electricity generation & consumption (EIA-906/EIA-920/EIA-923)', topics: [] },
				{ label: 'Sales of electricity (EIA-826/EIA-861)', topics: [] },
				{ label: 'Fuel quality & receipts (EIA-423/EIA-923)', topics: [] },

			];

			$.each(topics, function(index, topic) {
				switch (topic.dataTopicId.toString()) {
					case "0":
					case "1":
					case "2":
					case "3":
					case "4":
					case "8":
					case "9":
					case "10":
					case "57":
						topicGroups[0].topics.push(topic);
						break;
					case "5":
					case "6":
					case "7":
					case "56":
						topicGroups[1].topics.push(topic);
						break;
					case "11":
					case "12":
					case "13":
					case "14":
					case "15":
					case "16":
					case "17":
					case "43":
						topicGroups[2].topics.push(topic);
						break;
					default:
						topicGroups.push(topic);
						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().formReset.add(function(params) {
                var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME),
                    configProxy = self.facade.retrieveProxy(ConfigProxy.NAME),
                    topic = $.extend(true, {}, configProxy.getTopic(parameterProxy.getTopicId()));
                self.getViewComponent().hideForm();
                self.getViewComponent().showForm(
                    topic,
                    parameterProxy.getFormParams()
                );
            });
			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()));

				self.getViewComponent().showForm(
					topic,
					parameterProxy.getFormParams()
				);
			});
		},

		listNotificationInterests: function() {
			return this.$super().concat([
				Facade.RELOAD
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			switch (note.getName()) {
				case Facade.RELOAD:
					vc.hideForm();
					break;
				default:
					this.$super.handleNotification(note);
			}
		},

		onRemove : function() {
			this.getViewComponent().hideForm();
			this.getViewComponent().destroy();
		},

		__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/ParameterProxy'
], function(
	puremvc,
	Facade,
	ParameterProxy
) {
	return puremvc.Mediator.$extend({

		__init__: function(viewComponent) {
			this.$super(this.$class.NAME, viewComponent);
		},

		listNotificationInterests: function() {
			return this.$super().concat([
				Facade.CHART_PARAMS_UPDATED
			]);
		},

		handleNotification: function(note) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);

			switch (note.getName()) {
				case Facade.CHART_PARAMS_UPDATED:
					this.getViewComponent().updateSelection(parameterProxy.getChartType());
					break;
				default:
					this.$super(note);
			}
		},

		onRegister: function() {
			var self = this;

			/*this.getViewComponent().buttonClicked.add(function(params){
				self.getViewComponent().show();
			});*/
			this.getViewComponent().visualizationSelected.add(function(params) {
				self.sendNotification(Facade.CHART_SELECTION_UPDATED, {
					'ctype': params.newClass
				});
			});
		},

		__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.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();
			var arePinnedSeriesCharted;
			switch (note.getName()) {
				case Facade.TABLEDATA_UPDATED:
				case Facade.CHART_DATA_UPDATED:
					if (chartType == 'linechart' || chartType == 'pin' || chartType == 'sourcekey')
						arePinnedSeriesCharted = parameterProxy.arePinnedSeriesCharted('linechart');
					var userGenerated = parameterProxy.isUserGenerated('linechart');
					this.getViewComponent().update(
                        tableDataProxy.getVisualizationTitle(),
						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.DOWNLOAD_OPTION_SELECTED:
					if (note.body.target == 'linechart') {
						if (note.body.type == 'embed') {
							this.getViewComponent().embed();
						} else {
							this.getViewComponent().download(note.body.type);
						}
					}
					break;
				default:
					this.$super(note);
					break;
			}
		},

		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__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.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();
			var arePinnedSeriesCharted;

			switch (note.getName()) {
				case Facade.TABLEDATA_UPDATED:
				case Facade.CHART_DATA_UPDATED:

					if (chartType == 'columnchart' || chartType == 'pin' || chartType == 'sourcekey')
						arePinnedSeriesCharted = parameterProxy.arePinnedSeriesCharted('columnchart');
					var userGenerated = parameterProxy.isUserGenerated('columnchart');
					this.getViewComponent().update(
                        tableDataProxy.getVisualizationTitle(),
						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.DOWNLOAD_OPTION_SELECTED:
					if (note.body.target == 'columnchart') {
						if (note.body.type == 'embed') {
							this.getViewComponent().embed();
						} else {
							this.getViewComponent().download(note.body.type);
						}
					}
					break;
				default:
					this.$super(note);
					break;
			}
		},
		
		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {
			NAME: 'ColumnChartMediator'
		}
	});
});

define('js/view/MediatorFrequencySelectionButton',[
	'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);
		},

		listNotificationInterests: function() {
			return this.$super().concat([
				Facade.FREQUENCY_PARAM_UPDATED,
                Facade.TAB_PARAMS_UPDATED
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);

			switch (note.getName()) {
				case Facade.FREQUENCY_PARAM_UPDATED:
                    vc.updateSelection(parameterProxy.getFreq());
                    vc.toggleMenuEnabled(parameterProxy.enableFrequency());
					break;
                case Facade.TAB_PARAMS_UPDATED:
                    vc.toggleMenuEnabled(parameterProxy.enableFrequency());
                    break;
				default:
					this.$super(note);
					break;
			}
		},
		onRegister: function() {
			var self = this;

			this.getViewComponent().frequencySelected.add(function(params) {
				self.getViewComponent().hideMenu();
				self.sendNotification(Facade.FREQUENCY_SELECTION_UPDATED, {freq : params });
			});
		},

		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {
			NAME: 'FrequencyMediator'
		}
	});
});

define('js/view/MediatorTimePeriodRangeSelectionButton',[
	'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);
		},

		listNotificationInterests: function() {
			return this.$super().concat([
					Facade.TIME_PARAMS_UPDATED,
					Facade.TOPIC_PARAMS_UPDATED
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);

			switch (note.getName()) {
        case Facade.TIME_PARAMS_UPDATED:
					vc.updateTimeSelections({start : parameterProxy.getStart(), end : parameterProxy.getEnd() },parameterProxy.getFreq());
					break;
				case Facade.TOPIC_PARAMS_UPDATED:
					vc.updateConfig(configProxy.getTopic(parameterProxy.getTopicId(), parameterProxy.getFreq()));
					break;
				default:
					this.$super(note);
					break;
			}
		},


		onRegister: function() {
			var self = this;
			
			this.getViewComponent().timePeriodSelected.add(function(params) {
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var start, end;

				switch(parameterProxy.getFreq()){
					case "A":
						start = parseInt(params.start.year);
						end = parseInt(params.end.year);
						break;
					case "Q":
						start = (parseInt(params.start.year) * 100 + parseInt(params.start.quarter));
						end = (parseInt(params.end.year) * 100 + parseInt(params.end.quarter));
						break;
					case "M":
						start = (parseInt(params.start.year) * 100 + parseInt(params.start.month));
						end = (parseInt(params.end.year) * 100 + parseInt(params.end.month));
						break;
					default:
				}
				self.getViewComponent().hideMenu();
				self.sendNotification(Facade.TIME_SELECTION_UPDATED, {start : start, end : end});
			});

		},


		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {
			NAME: 'TimePeriodMediator'
		}
	});
});

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([
			]);
		},

		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()) {
				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'
], 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
			]);
		},

		handleNotification: function(note) {
			var data;
			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());

					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": data.DATA,
								"DATACOLUMNS": data.DATACOLUMNS,
								"PRECISION": data.PRECISION,
								"TITLE": data.TITLE,
								"UNITS": data.UNITS
							},
							parameterProxy.getMapType(),
							timeParams.start,
							timeParams.end,
							timeParams.freq, {
								'regionStyle': {
									hover: {
										stroke: '#FFFF96',
										"fill-opacity": 1,
										"stroke-width": 3,
										"stroke-opacity": 1
									}
								},
								'embedOptions': embedOptions
							},
							'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':
								tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
								mapDataProxy = this.facade.retrieveProxy(MapDataProxy.NAME);
								data = mapDataProxy.getData();
								var csvData = [
									['"' + tableDataProxy.getTableTitle() + ' ' + 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;
				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 = (value === null ? '--' : (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.SERIES_ID.split('.')[1] === 'PLANT') || !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>' : '';
		},

        hyperLinkCellFormatter: function(row, cell, value, columnDef, datum, dataContext) {
            var indent = datum.INDENT !== undefined && datum.INDENT ? '      ' : '';
            var isLink = columnDef.linkColumn != null ? datum[columnDef.linkColumn] != null : true;
            return (value !== '' && isLink) ? '<span class="link_cell">' + indent + value + '</span>' : indent + value;
        }
	};

	return self;
});


define('text!templates/pin_sourcekey_header.html',[],function () { return '<div class="pin_button {{#pin}} selected {{/pin}}">\n    <span class="text">Pin</span>\n</div>\n<div class="toggle_{{#pin}}pin{{/pin}}{{#sourcekey}}key{{/sourcekey}}">\n</div>\n<div class="api_button {{#sourcekey}} selected{{/sourcekey}}">\n    <span class="text">API</span>\n</div>\n\n';});

define('js/view/MediatorAggregateDataGrid',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/DateCodeUtil',
	'js/view/GridFormatters',
	'datagrid', 
	'text!templates/pin_sourcekey_header.html',
	'mustache'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
	TableDataProxy,
	DateCodeUtil,
	GridFormatters,
	DataGrid, 
	pin_sourcekey_header
) {
	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);

            $(this.getViewComponent().container).on('click', '.pinkey_header .slick-column-name > div', function(event) {
                var body = {};
                //body.pin = $(event.currentTarget).hasClass('pin_button') ? 0 : 1;
				body.ltype = $(event.currentTarget).hasClass('pin_button') ? 'pin' : 'sourcekey';
                self.sendNotification(Facade.PIN_KEY_SELECTION_UPDATED, body);
            });
		},

		listNotificationInterests: function() {
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.TIME_PARAMS_UPDATED,
				Facade.CHART_DATA_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED,
				Facade.CTYPE_PARAM_UPDATED,
				Facade.RSE_PARAM_UPDATED,
				Facade.PIN_KEY_PARAM_UPDATED
			]);
		},

		handleNotification: function(note) {
			var vc, self = this;
			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:
					tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					if (!tableDataProxy.isUpdating) {
						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') {
						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.CTYPE_PARAM_UPDATED:
					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:
					tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
					if (!tableDataProxy.isUpdating) {
						parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
						configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
						vc = this.getViewComponent();
						timeParams = parameterProxy.getTimeParams();
						data = tableDataProxy.getCombinedData();
						topic = configProxy.getTopic(parameterProxy.getTopicId());
						columns = this.generateColumns(
							tableDataProxy.getDataColumns(),
							data,
							timeParams.freq,
							tableDataProxy.getTableUnits(),
							topic,
							parameterProxy.getShowRSE()
						);
						vc.setColumns(columns);
					}
					break;
				case Facade.PIN_KEY_PARAM_UPDATED:
					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') {
							columns[index] = self.getPinSourcekeyColumn(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),
			sourcekey = parameterProxy.getLinkType(), options,
				columns = [this.getPinSourcekeyColumn(sourcekey), 
				{
					'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;
		},

		getPinSourcekeyColumn : function(ltype) {
			return {
					'id': 'pinKey',
					'field': 'HAS_DATA',
					'width': 30,
					'displayFormatter': GridFormatters.pinSeriesFormatter,
					'output': false,
					'cssClass': 'chart_column',
					'iconClass': ltype,
					'resizable': false,
                    headerCssClass : 'pinkey_header',
                    'name' : function(ltype) {
                        var options = ltype == 'sourcekey' ?  {'sourcekey':true} : {'pin':true};
                        return Mustache.render(pin_sourcekey_header, options);
                    }(ltype)
				};
		},
		
		dataGridClick: function(row, cell, datum) {
			var chartedObj;
			var vc = this.getViewComponent(),
				columns = vc.columns;
			var chartType = columns[cell].iconClass;
			if (columns[cell].id == 'chart' || columns[cell].id == 'pinKey') {
				if (datum.ACTION) {
					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) {
								chartedObj = {};
								chartedObj[chartType] = parameterProxy.getUpdatedCharted(datum.SERIES_ID, chartType);
								this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
							} else {
								tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
								var	children = tableDataProxy.getChildData(row),
									childrenIds = $.map(children, function(value) {
										return value.HAS_DATA ? value.SERIES_ID : null;
									});

								if (childrenIds.length > 0) {
									chartedObj = {};
									chartedObj[chartType] = parameterProxy.getUpdatedCharted(childrenIds, chartType, Boolean(datum[chartType]));
									this.sendNotification(Facade.CHART_SELECTION_UPDATED, chartedObj);
								}
							}
							break;
						case 'map':
							parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
								chartType = parameterProxy.getChartType();
							if (datum.HAS_DATA) {
								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 && !datum.NO_DATA_BREAKDOWN) {
					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');
				}
			}
		},

		onRemove : function() {
			this.getViewComponent().destroy();	
		},
		
		__classvars__: {

			NAME: 'DataGridMediator',

			linkCellFormatter: function(row, cell, value, columnDef, datum) {
				return (value !== '' && datum.filterQuery && !datum.NO_DATA_BREAKDOWN) ? '<span class="link_cell">' + value + '</span>' : value;
			},

			descriptionOutputFormatter: function(row, cell, value, columnDef, datum) {
				return datum.CHART_NAME || value;
			}
		}
	});
});


define('text!templates/main.html',[],function () { return '<div class="vis_container" id="show-hide-button-wrapper">\r\n\t<div class="show-note hide">Show visualizations</div>\r\n\t<div class="show-hide-button  button_down" title="Hide visualization"><span>-</span></div>\r\n\t<div id="chart_area"></div>\r\n</div>\r\n\r\n<div id="middle_topic_selection_container" class="topic_selection_container"></div>\r\n\r\n<div id="title_container" class="table_title"></div>\r\n\r\n<div class="controls-container">\r\n{{#mainDownloadButtonOpts}} {{>download_button}} {{/mainDownloadButtonOpts}}\r\n<div id="help_button" class="help">\r\n\t<span class="text">Help</span>\r\n</div>\r\n{{#mainFilterButtonOpts}} {{>filter_button}} {{/mainFilterButtonOpts}} {{#mainAnalysisButtonOpts}} {{>analysis_button}} {{/mainAnalysisButtonOpts}} {{#mainVisualizationButtonOpts}} {{>visualization_button}} {{/mainVisualizationButtonOpts}}\r\n{{#comparePlantsButtonOpts}}{{>compare_plants_button}}{{/comparePlantsButtonOpts}}\r\n{{#searchPlantsButtonOpts}}{{>search_plants_button}}{{/searchPlantsButtonOpts}}\r\n</div>\r\n<div class="clear"></div>\r\n{{#showSearch}}\r\n<div id="search_time_container">\r\n\t<div id="search_container"></div>\r\n</div>\r\n{{/showSearch}}\r\n\r\n<div id="time-selection-controls" class="time-selection-controls">\r\n\t<div id="frequency-selection-button-container" class="selection-menu-button frequency">\r\n\t\t{{>frequency_selection_button}}\r\n\t</div>\r\n\t<div id="time-period-selection-button-container" class="selection-menu-button frequency">\r\n\t\t{{>time_period_selection_button}}\r\n\t</div>\r\n</div>\r\n<div id="tab-container">\r\n    {{#showTabs}}{{>tabs}}{{/showTabs}}\r\n</div>\r\n<div class="clear"></div>\r\n<div id="tableContainer"></div>\r\n<div class="clear"></div>\r\n<div id="footnote_container" class="footnote-container">\r\n</div>\r\n<div class="clear"></div>\r\n<div id="filter_dialog_container">\r\n\t<div id="filter_dialog"></div>\r\n</div>\r\n<div id="download_dialog_wrap">\r\n\t<div id="download_dialog_container">\r\n\t\t<div id="download_dialog"></div>\r\n\t</div>\r\n</div>\r\n<div id="frequency_dialog_container">\r\n\t<div id="frequency_dialog"></div>\r\n</div>\r\n<div id="time_dialog_container">\r\n\t<div id="time_dialog"></div>\r\n</div>\r\n<div id="analysis_dialog_wrap">\r\n\t<div id="analysis_dialog_container">\r\n\t\t<div id="analysis_dialog"></div>\r\n\t</div>\r\n</div>\r\n<div id="plant_selection_dialog_wrap">\r\n    <div id="plant_selection_dialog_container">\r\n        <div id="plant_selection_dialog"></div>\r\n    </div>\r\n</div>\r\n<div id="mask"></div>\r\n<!-- this will be a placeholder button element until i can change it to the pure mvc alternative -->\r\n';});


define('text!templates/filter_button.html',[],function () { return '{{#selected}}\r\n<div {{#id}} id="{{id}}" {{/id}} class="selection-menu-button 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<span class="text">Filter<br>/order</span>\r\n</div>\r\n{{/selected}}\r\n';});


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


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


define('text!templates/analysis_button.html',[],function () { return '<div {{#id}} id="{{id}}" {{/id}} class="selection-menu-button 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<span class="text">Related<br/>analysis</span>\r\n</div>\r\n';});


define('text!templates/compare_plants_button.html',[],function () { return '<div {{#id}} id="{{id}}" {{/id}} class="selection-menu-button compare-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\r\n\t">\r\n  <span class="text">Compare<br>Plants</span>\r\n</div>\r\n';});


define('text!templates/search_plants_button.html',[],function () { return '<div {{#id}} id="{{id}}" {{/id}} class="selection-menu-button search-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\r\n\t">\r\n\t<div class="line"></div>\r\n  <span class="text">Search<br>Plants</span>\r\n</div>\r\n';});


define('text!templates/tabs.html',[],function () { return '<div id="tab-container">\r\n    <div id="selection-container">\r\n        {{#tabs}}\r\n        <div id="{{tabId}}">\r\n            {{#tabList}}\r\n            <input type="radio" id="{{id}}" name="{{name}}" value="{{value}}">\r\n            <label for="{{id}}" class="ui-button">{{label}}</label>\r\n            {{/tabList}}\r\n        </div>\r\n        {{/tabs}}\r\n    </div>\r\n    {{#subTabs}}\r\n    <div id="{{selector}}" class="selector_container" style="display: none;">\r\n        <div class="seperator">{{seperator}}</div>\r\n        <div id="{{tabId}}">\r\n            {{#tabList}}\r\n            <input type="radio" id="{{id}}" name="{{name}}" value="{{value}}">\r\n            <label for="{{id}}" class="ui-button">{{label}}</label>\r\n            {{/tabList}}\r\n        </div>\r\n    </div>\r\n    {{/subTabs}}\r\n</div>\r\n';});


define('text!templates/frequency_selection_button.html',[],function () { return '<div class="text"></div>\r\n';});


define('text!templates/time_period_selection_button.html',[],function () { return '<div class="text"></div>\r\n';});

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',
	'text!templates/compare_plants_button.html',
	'text!templates/search_plants_button.html',
	'text!templates/tabs.html',
	'text!templates/frequency_selection_button.html',
	'text!templates/time_period_selection_button.html',
	'signals'
], function(
	Class,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	visualizationsButtonTemplate,
	analysisButtonTemplate,
	comparePlantsButtonTemplate,
	searchPlantsButtonTemplate,
	tabsTemplate,
	frequencySelectionButtonTemplate,
	timePeriodSelectionButtonTemplate,
	signals
) {
	var chartOn = true;
	var chartCookie = 'chartCookie';

	return Class.$extend({

		container: null,

		__init__: function(container, options) {
			var self = this;
			this.container = container;

			this.container.empty().html(
				Mustache.render(pageTemplate, options, {
					'filter_button': filterButtonTemplate,
					'download_button': downloadButtonTemplate,
					'visualization_button': visualizationsButtonTemplate,
					'analysis_button': analysisButtonTemplate,
					'compare_plants_button': comparePlantsButtonTemplate,
					'search_plants_button': searchPlantsButtonTemplate,
					'tabs': tabsTemplate,
					'frequency_selection_button': frequencySelectionButtonTemplate,
					'time_period_selection_button': timePeriodSelectionButtonTemplate
				})
			);

            $(document).ready(function(){
				$(".show-hide-button").click(function(event){
					if (chartOn) { // Toggle Close
						chartOn = false;
						createCookie(chartCookie,false,1); //creating cookie for visualization state.
						$(".show-note").toggleClass("hide");
                        $("#chart_area").removeClass("slidedown").addClass("slideup");
						$(".show-hide-button").delay(400).queue(function(){
							$(".show-hide-button").attr("title", "Show visualization");
							$(".show-hide-button").toggleClass("button_up button_down");
							$(".show-hide-button > span").text("+");
							$(".show-hide-button").dequeue();
						});
					} else { // Toggle Open
						chartOn = true;
						createCookie(chartCookie,true,1); //creating cookie for visualization state.
						$(".show-note").toggleClass("hide");
                        $("#chart_area").removeClass("slideup").addClass("slidedown");
						$(".show-hide-button").delay(400).queue(function(){
							$(".show-hide-button").attr("title", "Hide visualization");
							$(".show-hide-button").toggleClass("button_up button_down");
							$(".show-hide-button > span").text("-");
							$(".show-hide-button").dequeue();
						});
					}
				});

				//Reading cookie for visualization state
				var check = readCookie(chartCookie);
				if(check == 'true'){
					chartOn = true;
					$(".show-note").toggleClass("show");
                    $("#chart_area").removeClass("slideup").addClass("slidedown");
					$(".show-hide-button").delay(400).queue(function(){
						$(".show-hide-button").attr("title", "Hide visualization");
						$(".show-hide-button").toggleClass("button_up button_down");
						$(".show-hide-button > span").text("-");
						$(".show-hide-button").dequeue();
					});

				}
				if (check == 'false'){
					chartOn = false;
                    $(".show-note").toggleClass("hide");
                    $("#chart_area").removeClass("slidedown").addClass("slideup");
					$(".show-hide-button").delay(400).queue(function(){
						$(".show-hide-button").attr("title", "Show visualization");
						$(".show-hide-button").toggleClass("button_up button_down");
						$(".show-hide-button > span").text("+");
						$(".show-hide-button").dequeue();
					});
				}

				var currentState;
				$.each($(".ui-button").toArray(), function(key, value){
					$(value).click(function(){
						currentState = $(value).attr('for');
						console.log(currentState);
					});
				});
				$(".ui-button").mouseover(function() {
	 				$(this).removeClass("ui-state-hover");
				});
			});
		},
		getFormContainer: function() {
			return $('#filter_dialog', this.container);
		},

		getTabContainer: function() {
			return $('#tab-container', this.container);
		},

		getFrequencyContainer: function() {
			return $('#frequency_dialog_container', this.container);
		},

		getTimePeriodContainer: function() {
			return $('#time_dialog_container', this.container);
		},

		getVisualizationContainer: function() {
			return $('#main_visualization_button', this.container);
		},

		getPinKeyVisualizationContainer: function() {
			return $('#pinkey_container', this.container);
		},

		getDownloadContainer: function() {
			return $('#download_dialog', 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);
		},

        getPlantSelectionContainer: function() {
            return $('#plant_selection_dialog', 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);
		}
	});

	//Create/Read Cookie functions. name = string, value = string, days = number.
	function createCookie(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires;
	}

	function readCookie(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0)
				return c.substring(nameEQ.length,c.length);
		}
		return null;
	}

});


define('text!templates/filterBox.html',[],function () { return '<div>\r\n\t<h2>Filter / Order</h2>\r\n\t<hr/>\r\n\t<div id="filters"></div>\r\n\t{{#isPlantList}}\r\n\t<hr>\r\n\t<div>\r\n\t\t<button id="add-filter" class="add_filter">+ Add Filter</button>\r\n\t</div>\r\n\t{{/isPlantList}}\r\n\t<hr>\r\n\t<div class="filter_buttons">\r\n\t\t<span id="submitButton" class="form-buttons">\r\n\t\t\t<span>Submit</span>\r\n\t\t</span>\r\n\t\t<span id="resetButton" class="form-buttons">\r\n\t\t\t<span>Reset</span>\r\n\t\t</span>\r\n\t\t<span id="cancelButton" class="form-buttons">\r\n\t\t\t<span>Cancel</span>\r\n\t\t</span>\r\n\t</div>\r\n</div>\r\n';});


define('text!templates/filter.html',[],function () { return '<div class="filter-container closed">\r\n\t<div>\r\n\t\t<div class="filter-number">{{number}}</div>\r\n\t\t{{#isNotPlantList}}\r\n\t\t<div class="filter-label"></div>\r\n\t\t{{/isNotPlantList}}\r\n\t\t<div class="filter-label" style="display:none"></div>\r\n\r\n\t\t<div class="select_arrow {{#isNotPlantList}}hidden{{/isNotPlantList}}">\r\n\t\t\t<select>\r\n\t\t\t\t<option disabled selected value>select filter</option>\r\n\t\t\t\t{{#aggColumns}}\r\n\t\t\t\t<option value="{{aggColumnId}}">{{description}}</option>\r\n\t\t\t\t{{/aggColumns}}\r\n\t\t\t</select>\r\n\t\t</div>\r\n\t\t<div class="filter-summary no-selection">No filters selected</div>\r\n\t\t<div class="handle"></div>\r\n\t\t{{#isPlantList}}<div class="remove">remove</div>{{/isPlantList}}\r\n\t\t<div class="edit">edit</div>\r\n\t\t\r\n\t</div>\r\n\t<div class="select-clear-all"><span class="select-all">Select all</span>|<span class="clear-all">Clear all</span></div>\r\n\t<div class="filter-values-container"></div>\r\n</div>';});


define('text!templates/selectionItem.html',[],function () { return '<input type="checkbox" value="{{valueId}}" name="{{filterGroupId}}" data-filtervalue="{{filterValue}}" id="filter-{{filterGroupId}}-{{filterValue}}"/>\n<label for="filter-{{filterGroupId}}-{{filterValue}}"><span class="filter-item">{{description}}</span></label>\n';});

String.prototype.padStart = String.prototype.padStart ? String.prototype.padStart : function(targetLength, padString) {
    targetLength = Math.floor(targetLength) || 0;
    if(targetLength < this.length) return String(this);

    padString = padString ? String(padString) : " ";

    var pad = "";
    var len = targetLength - this.length;
    var i = 0;
    while(pad.length < len) {
        if(!padString[i]) {
            i = 0;
        }
        pad += padString[i];
        i++;
    }

    return pad + String(this).slice(0);
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = String.prototype.padStart;
};
define("string.padstart", function(){});

define('js/view/FilterSerializers',['string.padstart'], function() {
    var self = {
        deserializeFilter : function deserializeFilter(filterString) {
            var base32Array = filterString.split(''),
                binaryString,
                i,
                j,
                ret = [];

            for(i = 0; i < base32Array.length; i++) {
                binaryString = parseInt(base32Array[i], 32).toString(2).padStart(5, "0");
                for(j = 0; j < binaryString.length; j++) {
                    if(binaryString[j] === "1"){
                        ret.push(i * 5 + j);
                    }
                }
            }

            return ret;
        }
    };

    return self;
});

define('js/view/VC_Form',[
		'classy',
		'text!templates/filterBox.html',
		'text!templates/filter.html',
		'text!templates/selectionItem.html',
        'js/view/FilterSerializers',
		'signals',
		'mousewheel',
		'mustache',
		'simpleModal',
        'string.padstart'
	],
	function(Class, filterBoxTemplate, filterTemplate, selectionItemTemplate, FilterSerializers, signals) {
		var no_selection_text = "", filterValueSelected;

		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,
					filterValue: filterValues[i].filterValue
				};
				txt.description = txt.description.toString().charAt(0).toUpperCase() + txt.description.toString().slice(1);
				ret += '<li>' + Mustache.to_html(selectionItemTemplate, txt) + ((nextLevel <= currentLevel) ? '</li>' : '');

				previousLevel = currentLevel;
			}
			return ret;
		};

		var cancelButtonHandler = function(event) {
			event.data.dispatch();
		};

		var resetButtonHandler = function(event) {
			event.data.dispatch();
		};

		//Disables previously selected options
		var disableSelectedOptions = function(){
			$('option').prop('disabled',false);
			$('select').each(function(){
				var select = $(this),
					options = $('select').not(select).find('option'),
					selectedText = select.children('option:selected').text();
				options.each(function(){
					if($(this).text() == selectedText){
						$(this).prop('disabled',true);
					}
					if($(this).text() == 'select filter'){
						$(this).prop('disabled',true);
					}

				});
			});
		};

		var buildSubmitObject = function(formOptions) {
			var ret = {};

			// Build agg variable
			var agg = [];
			var singleMultiSelects = "",
				multiMultiSelects = "";

			// set filters to empty strings so any unused filters get cleared
			for(var i = 0; i < formOptions.aggColumns.length; i++) {
				ret[formOptions.aggColumns[i].filterGroup.varName] = "";
			}
			$.each($('.filter-container'), function(index) {
                if ($(this).data('aggColumnId') !== undefined) {
                    agg.push($(this).data('aggColumnId'));
                }
            });

			ret.agg = agg.join(',');

			// Build filters from checkboxes
			$('.filter-values-container').each(function() {
				var varName = $(this).data('varName');
				var controls = $('input:checked', $(this));
				var checkedArray = [];
				controls.each(function() {
					checkedArray[$(this).val()] = 1;
				});

				var binaryString = "";

				ret[varName] = "";

				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[varName] = ret[varName] + parseInt(binaryString, 2).toString(32);
						binaryString = "";
					}
				}
				ret[varName] = (ret[varName] === '') ? "g" : ret[varName];

			});

			return ret;
		};

		function populateFilters(container, formOptions, hashData) {
			if(hashData.agg == "") {
				return;
			}
			var i, j,
					aggArray = hashData.agg.toString().split(','),
					aggColumnByKey = {},
					filterString,
					filterContainer,
					selectedValues = [];

			for(i = 0; i < formOptions.aggColumns.length; i++) {
				aggColumnByKey[formOptions.aggColumns[i].aggColumnId] = formOptions.aggColumns[i];
			}

			for(i = 0; i < aggArray.length; i++) {
				addNewFilter(container, formOptions);

				filterContainer = $('.filter-container', container).last();
				filterContainer.toggleClass(aggColumnByKey[aggArray[i]].filterGroup.varName, true);
				$('select', filterContainer).val(aggArray[i]).change();
				$('.filter-label', filterContainer).text(aggColumnByKey[aggArray[i]].description);

				filterString = hashData[aggColumnByKey[aggArray[i]].filterGroup.varName];
				if(typeof filterString !== 'undefined' && filterString !== "") {
					selectedValues = FilterSerializers.deserializeFilter(filterString);
				}

				for(j = 0; j < selectedValues.length; j++) {
					$('input:checkbox[value=' + selectedValues[j] + ']', filterContainer).prop('checked', true);
					if(j == selectedValues.length - 1) {
						$('input:checkbox[value=' + selectedValues[j] + ']', filterContainer).change();
					}
				}
				updateFilterSummary(filterContainer, formOptions.isPlantList);
			}

			updateFilterNumbers(container, formOptions.isPlantList);

			filterContainer.toggleClass("open", false).toggleClass("closed", true);
            $('.edit', filterContainer).text('edit');
		}
		function addNewFilter(container, formOptions) {
			var filterNumber = $(".filter-container", container).length + 1;
			container.append($(Mustache.to_html(filterTemplate, {
				"number" : filterNumber,
				"aggColumns" : formOptions.aggColumns,
				"isNotPlantList" : !formOptions.isPlantList,
				"isPlantList" : formOptions.isPlantList
			})));
			$('.filter-container', container)
					.toggleClass("open", false).toggleClass("closed", true);

			//Disabling options that have been previous selected
			 disableSelectedOptions();

			no_selection_text = $('.filter-summary', $('.filter-container', container).last()).text();
			var sortableElement = $('#filters', container);
			if(sortableElement.hasClass('ui-sortable')) {
				sortableElement.sortable('refresh');
			}
			//Disabling the "Add Filter" button when all options are selected
			if(filterNumber >= formOptions.aggColumns.length){
				$('#add-filter, hr').hide();
			}
		}

		function updateFilterNumbers(container, isPlantList) {
			$('.filter-container .filter-number', container).each(function updateNumbers(index, element) {
				$(element).text(index+1);

				if(!isPlantList){
					if(index === 0) {
						$('input', $(element).parents('.filter-container')).attr('type', 'radio');
						$('.select-clear-all', $(element).parents('.filter-container')).toggleClass('hidden', true);
					}
					else {
						$('input', $(element).parents('.filter-container')).attr('type', 'checkbox');
						$('.select-clear-all', $(element).parents('.filter-container')).toggleClass('hidden', false);
					}
				}
			});
		}

        function removeFilter(container, event, formOptions) {
			$(event.target).parents('.filter-container').remove();

			updateFilterNumbers(container, formOptions.isPlantList);

			disableSelectedOptions();

			if($('.filter-container .filter-number', container).length === 0) {
				addNewFilter(container, formOptions);
			}
			if(container.hasClass('ui-sortable')) {
				container.sortable('refresh');
			}
			//Enabling "Add Filter" button if not all options are selected.
			var filterNumber = $(".filter-container", container).length;
			if(filterNumber < formOptions.aggColumns.length){
				$('#add-filter, hr').show();
			}
            event.stopPropagation();
		}

		function editFilter(event) {
			var container = $(event.target).parents('#filters');

            var isOpening = $(event.target).parents('.filter-container').hasClass("closed");

            // set all filters to closed
			$('.filter-container', container)
					.toggleClass("open", false).toggleClass("closed", true);
            $('.filter-container .edit', container).text('edit');

            // set clicked one to open
            if(isOpening) {
                $(event.target).parents('.filter-container').toggleClass("open", true).toggleClass("closed", false);
                $('.edit', $(event.target).parents('.filter-container')).text('hide');
            }
		}

		function changeFilter(selectedFilter, filterContainer, formOptions) {
			var aggColumn;

			if(isNaN(selectedFilter)) {
				return;
			}

			//Disabling options that have been previous selected
			disableSelectedOptions();

			for(var i = 0; i < formOptions.aggColumns.length; i++) {
				filterContainer.toggleClass(formOptions.aggColumns[i].filterGroup.varName, false);
				if(formOptions.aggColumns[i].aggColumnId == selectedFilter) {
					aggColumn = formOptions.aggColumns[i];
					filterContainer.toggleClass(aggColumn.filterGroup.varName, true);
				}
			}

			filterContainer.data('aggColumnId', aggColumn.aggColumnId);

			$('.filter-values-container', filterContainer)
					.empty()
					.append($(Mustache.to_html("<ul>{{{filterTreeFunction}}}</ul>", aggColumn)))
					.data('varName', aggColumn.filterGroup.varName);

			filterContainer.siblings().toggleClass("open", false).toggleClass("closed", true);
            $('.edit', filterContainer.siblings()).text('edit');

			filterContainer.toggleClass("open", true).toggleClass("closed", false).toggleClass("selected", true);
            $('.edit', filterContainer).text('hide');

			updateFilterSummary(filterContainer, formOptions.isPlantList);

			//Setting select to static text once option is selected.
			if(formOptions.isPlantList) {
				var description = filterContainer.find('option:selected').text();
				$('.filter-label', filterContainer).text(description).show();
				$('.select_arrow', filterContainer).hide();
			}

		}

		function selectAllFilterValues(event) {
			var filterContainer = $(event.target).parents('.filter-container');

			$('input', filterContainer).prop('checked', true);

			updateFilterSummary(filterContainer);
		}

		function clearAllFilterValues(event) {
			var filterContainer = $(event.target).parents('.filter-container');
			$('input', filterContainer).prop('checked', false);

			updateFilterSummary(filterContainer);
		}

		function updateFilterSummary(container, isPlantList) {
			var checked = $('input:checked', container);

			if(checked.length === 0) {
				$('.filter-summary', container)
						.toggleClass('no-selection', true)
						.text(no_selection_text);
			}
			else {
				var summaryText = [];
				var childrenToSkip = [];
				checked.each(function buildSummaryText(index, element) {
					// skip children if it is a parent, push children into array for tracking
					$('input', $(element).siblings('ul')).each(function(i, inputElement) {
						childrenToSkip.push($(inputElement).val());
					});
					if(!isPlantList || childrenToSkip.indexOf($(element).val()) === -1){
						summaryText.push($('> label', $(element).parent()).text().trim());
					}
				});
				$('.filter-summary', container)
						.toggleClass('no-selection', false)
						.text(summaryText.join(', '));
			}
		}

		function checkChildren(checkboxElement) {
			$('input', checkboxElement.siblings('ul'))
					.prop('checked', checkboxElement.is(":checked"))
					.prop('indeterminate', false);
		}

		function checkParents(checkboxElement) {
			var filterGroupName = checkboxElement.prop("name"),
					parentInput = checkboxElement.parents('ul:first').siblings('input[name="' + filterGroupName + '"]'),
					siblingInputs, indeterminate, checked;

			while(parentInput.length > 0) {
				siblingInputs = $('> li > input', parentInput.siblings('ul'));
				indeterminate = (siblingInputs.filter(':checked').length > 0 || siblingInputs.filter(function () { return (this.indeterminate === true); }).length > 0) && siblingInputs.filter(':checked').length !== siblingInputs.length;
				checked = siblingInputs.filter(':checked').length === siblingInputs.length;

				parentInput.prop('checked', checked);
				parentInput.prop('indeterminate', indeterminate);

				parentInput = parentInput.parents('ul:first').siblings('input[name="' + filterGroupName + '"]');
			}

		}

		var VC_Form = 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();
				});
				this.modalCloseHandler = function(e) {
					if ($(e.target).parents('#filter_dialog_container').length === 0) {
						self.hideForm();
					}
				};
			},

			generateForm: function(formOptions, hashData) {
				var self = this;
				// property to indicate whether to make parent checkboxes use a 3 way state or 2 way state
				this.isPlantList = formOptions.dataTopicId == 1;
				formOptions.isPlantList = this.isPlantList;

				// 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));

				this.container.empty();
				this.container.append(filterMenu);

				this.addFilterClickHandler = function(event) {
					addNewFilter($('#filters', self.container), formOptions);
				};

				this.removeClickHandler = function(event) {
					removeFilter($(event.target).parents('#filters'), event, formOptions);
				};

				this.filterChangeHandler = function(event) {
					changeFilter(
							parseInt($(event.target).val()),
							$(event.target).parents('.filter-container'),
							formOptions
					);
				};

				this.submitButtonHandler = function(event) {
					var subObj = buildSubmitObject(formOptions);
					event.data.dispatch(subObj);
				};

				filterValueSelected = function(event) {
					var target = this;
					var filterContainer = $(event.target).parents('.filter-container');
					var checked = $(this).prop('checked');
					var siblingCheckBoxes = $('input:checkbox[value="' + $(this).val() + '"]', filterContainer);

					$.each(siblingCheckBoxes, function(index, element) {
						if (element !== target) {
							$(element).prop('checked', checked);
						}
					});

					if(self.isPlantList || filterContainer.index() !== 0) {
						checkChildren($(this));
						checkParents($(this));
					}

					updateFilterSummary(filterContainer, self.isPlantList);
				};

				$("#add-filter", this.container).on('click', this.addFilterClickHandler);
				$("#filters", this.container).on('click', '.filter-container .remove', this.removeClickHandler);
				$("#filters", this.container).on('click', '.filter-container .edit', editFilter);
				$("#filters", this.container).on('click', '.filter-container .select-all', selectAllFilterValues);
				$("#filters", this.container).on('click', '.filter-container .clear-all', clearAllFilterValues);
				$("#filters", this.container).on('change', '.filter-container input', filterValueSelected);
				$("#filters", this.container).on('change', 'select', this.filterChangeHandler);

				// Attach event to submit button
				filterMenu.find('#submitButton').on('click', this.formSubmitted, this.submitButtonHandler);
				filterMenu.find('#cancelButton').on('click', this.formCanceled, cancelButtonHandler);
				filterMenu.find('#resetButton').on('click', this.formReset, resetButtonHandler);

				if(hashData.agg !== "" || !this.isPlantList) {
					populateFilters($('#filters', this.container), formOptions, hashData);
				}
				else {
					addNewFilter($('#filters', this.container), formOptions);
				}

				$('#filters', this.container).sortable({
					handle: '.handle',
					items: '.filter-container',
					axis: 'y',
					start: function (event, ui) {
						$(ui.item[0]).find('.handle').toggleClass('grabbed');
					},
					stop: function (event, ui) {
						$(ui.item[0]).find('.handle').toggleClass('grabbed');	
					},
					update: function (event, ui) {
						updateFilterNumbers($(event.target), formOptions.isPlantList);
						$.each($('.filter-container', self.container), function(index, element) {
							updateFilterSummary($(element), formOptions.isPlantList);
						});
					}
				}).on("mousedown", ".handle", function() { //Collapses filter container when sorting.
					$('.filter-container', self.container).toggleClass("open", false).toggleClass("closed", true);
					$('.filter-container .edit', self.container).text('edit');
				});
			},

			showForm: function(config, formParams) {
				var buttonOffset = this.button.position(),
					self = this;

				this.generateForm(
					config,
					formParams
				);

				var filterDialogContainer = $('#filter_dialog_container');

				$('html').css({
					overflow : 'hidden'
				});
				$('#mask').css({
						height: $(document).height(),
						width: $(document).width()
					})
					.show();

				// 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);
				});

				filterDialogContainer
						.show()
						.css({
							top : $('header').height() + 15,
							maxHeight : '90vh',
							maxWidth : $(window).width() - ($('#filter_dialog_container').position().left * 2)
						});

				$(window).resize(this.resizeCallback = function() {
					$('#mask').css({
						height: $(document).height(),
						width: $(document).width()
					});
					$('#filter_dialog_container')
							.css({ 
                                top : $('header').height() + 15,
								maxHeight : '90vh',
								maxWidth : $(window).width() - ($('#filter_dialog_container').position().left * 2)
							});
				});

				this.opened = true;
			},

			hideForm: function() {
				$('#mask').hide();
				$('#filter_dialog_container').hide();
				$('html').css({ overflow : 'auto' });

                $('#filters', this.container).sortable('destroy');
				this.container.empty();
				if (typeof this.resizeCallback == 'function') {
					$(window).unbind('resize', this.resizeCallback);
				}
				if (typeof this.modalCloseHandler == 'function') {
					$('body').unbind('click', this.modalCloseHandler);
				}
				
				this.resizeCallback = null;
				this.opened = false;
			},

			destroy: function() {
				$("#add-filter", this.container).off('click', this.addFilterClickHandler);
				$("#filters", this.container).off('click', '.filter-container .remove', this.removeClickHandler);
				$("#filters", this.container).off('click', '.filter-container .edit', editFilter);
				$("#filters", this.container).off('click', '.filter-container .select-all', selectAllFilterValues);
				$("#filters", this.container).off('click', '.filter-container .clear-all', clearAllFilterValues);
				$("#filters", this.container).off('change', '.filter-container input', filterValueSelected);
				$("#filters", this.container).off('change', 'select', this.filterChangeHandler);

				// Attach event to submit button
				this.container.find('#submitButton').off('click', this.formSubmitted, this.submitButtonHandler);
				this.container.find('#cancelButton').off('click', this.formCanceled, cancelButtonHandler);
				this.container.find('#resetButton').off('click', this.formReset, resetButtonHandler);
                $('#filters', this.container).sortable('destroy');
			}
		});

		return VC_Form;
	});


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

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.position();

				this.generateDownload(
					this.container,
					config
				);

				$('#download_dialog_wrap').toggle().css({
					'top': (buttonOffset.top + 31),
					'left': buttonOffset.left,
					'position': 'absolute',
					'z-index': '1200'
				});
				$("#main_download_button").click(function(e) {
					e.stopPropagation();
				});

				// 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);
				});

				$('#download_dialog_container').css({
					left: this.button.outerWidth() - this.container.outerWidth()
				});

				$(window).resize(this.resizeCallback = function() {
					var buttonOffset = self.button.position();
					$('#download_dialog_container').css({
						'top': buttonOffset.top,
						'left': buttonOffset.left
					});
				});
			},

			hideDownload: function() {
				$('#download_dialog_wrap').toggle(false);
				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}}-radio" name="{{name}}" value="{{class}}" />\r\n<label for="{{class}}-radio" alt="{{description}}" title="{{description}}" class="{{class}}">{{{label}}}</label>\r\n{{/items}}\r\n\r\n';});

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.container = container;
				this.config = $.extend({}, (config ? config : this.$class.defaultConfig));
				this.items = this.config.items;

				this.visualizationSelected = new signals.Signal();
				this.generateMenu(
					this.container,
					$.extend(this.config, config)
				);

				$(this.container).buttonset();
				this.visualizationSelectors = $('input', this.container);
				this.updateSelectionIndex(this.config.currentSelectionIndex ? this.config.currentSelectionIndex : 0);

				this.visualizationSelectors.change(function(event) {
					self.updateSelection(this.value);
					var item = self.config.items[self.currentSelectionIndex];
					self.visualizationSelected.dispatch({
						'newClass': item.class,
						'item': item,
						'event': event
					});
				});
			},

			generateMenu: function(container, options, hashData) {
				var menu = $(Mustache.to_html(boxTemplate, { items: this.items }));

				container.empty().append(menu);
			},

			updateSelection: function(className) {
				var self = this;

				$.each(this.config.items, function(index, value) {
					if (value.class == className) {
						self.updateSelectionIndex(index);
					}
				});
			},

			updateSelectionIndex: function(index) {
				var self = this;
				this.currentSelectionIndex = index;
				$.each(this.config.items, function(index, value) {
					if (index == self.currentSelectionIndex) {
						$('input[value=' + value.class + ']', self.container).attr('checked', true);
					}
				});
				$(this.container).buttonset('refresh');
			},

			__classvars__: {
				defaultConfig: {
					items: [{
						'name': 'visualizations',
						'class': 'linechart',
						'description': 'Time-series chart',
						'label': 'Time-<br>series'
					}, {
						'name': 'visualizations',
						'class': 'columnchart',
						'description': 'Column chart',
						'label': 'Column<br>chart'
					}, {
						'name': 'visualizations',
						'class': 'map',
						'description': 'U.S. map over time',
						'label': 'Heat<br>map'
					}],
					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<form id="data_set" name="data_set">\r\n\t\t<!---label id="topic_select_label" for="topic_select">Change Data Set: </label--->\r\n\t\t<span class="select_arrow">\r\n\t\t\t<select id="topic_select" size="1" name="topic_select" class="link_prompt">\r\n\t\t\t\t{{#topics}} {{#label}}\r\n\t\t\t\t<optgroup label="{{label}}">\r\n\t\t\t\t\t{{#topics}}\r\n\t\t\t\t\t<option class="link_option" value="{{dataTopicId}}">{{description}}</option>\r\n\t\t\t\t\t{{/topics}}\r\n\t\t\t\t</optgroup>\r\n\t\t\t\t{{/label}} {{^label}}\r\n\t\t\t\t<option class="link_option" value="{{dataTopicId}}">{{description}}</option>\r\n\t\t\t\t{{/label}} {{/topics}}\r\n\t\t\t</select>\r\n\t\t</span>\r\n\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<form id="choose_report" name="choose_report">\r\n\t\t<span class="select_arrow">\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</span>\r\n\t</form>\r\n</div>\r\n<div class="clear"></div>\r\n';});

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') {
					var clickHandler = function(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('text!templates/frequency_selection_box.html',[],function () { return '<div class="frequency-menu-container">\r\n\t<div id="frequency-menu">\r\n\t\t<label for="annual">Annual</label>\r\n\t\t<input name="frequency" id="annual" type="radio" value="A">\r\n\t\t<label for="monthly">Monthly</label>\r\n\t\t<input name="frequency" id="monthly" type="radio" value="M">\r\n\t\t<label for="quarterly">Quarterly</label>\r\n\t\t<input name="frequency" id="quarterly" type="radio" value="Q">\r\n\t</div>\r\n</div>\r\n';});

define('js/view/VC_Frequency_Selection_Button',[
			'classy',
			'text!templates/frequency_selection_box.html',
			'signals',
			'mustache',
			'simpleModal'
		],
		function(Class, frequencyBoxTemplate, signals) {
			return Class.$extend({

				__init__: function(container) {
					var self = this;

					this.container = container;
					this.button = $('#frequency-selection-button-container');
					this.frequencySelected = new signals.Signal();

					this.container.empty().append($(Mustache.to_html(frequencyBoxTemplate)));
					this.frequencyButtonset = $('#frequency-menu', this.container);
					this.frequencyButtonset.buttonset();

					this.buttonClickHandler = function(e) {
						if (!(self.button.hasClass('disabled') || self.button.hasClass('open'))) {
							self.showMenu();
						}
					};

					this.button.click(this.buttonClickHandler);

					this.frequencySelectedHandler = function(event){
						self.hideMenu();
						self.frequencySelected.dispatch($(event.target).val());
					};
					$(this.container).on('change', 'input[name="frequency"]',this.frequencySelectedHandler );

					this.modalCloseHandler = function(e) {
						if ($(e.target).parents('#frequency_dialog_container').length === 0) {
							self.hideMenu();
						}
					};
				},

				updateSelection : function(frequency) {
					this.updateButtonLabel(frequency);

					$('input[value="' + frequency + '"]', this.container).prop('checked', true);
					this.frequencyButtonset.buttonset('refresh');
				},

				updateButtonLabel : function(frequency) {
					var textLabel = "";

					switch(frequency) {
						case 'A':
							textLabel = 'Annual';
							break;
						case 'M':
							textLabel = 'Monthly';
							break;
						case 'Q':
							textLabel = 'Quarterly';
							break;
					}

					$('.text', this.button).text(textLabel);
				},

				showMenu: function(currentValue) {

					var self = this;
						this.button.toggleClass("open", true);
					var buttonOffset = {
                        'top' : self.button.position().top + self.button.offsetParent().position().top,
                        'left' : self.button.position().left + self.button.offsetParent().position().left
                    };

					this.container
							.css({
								'top': buttonOffset.top + this.button.height(),
								'left': buttonOffset.left - (this.container.width() - this.button.width())
							})
							.show();

					// 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);
					});

					$(window).resize(this.resizeCallback = function() {
                        var buttonOffset = {
                            'top' : self.button.position().top + self.button.offsetParent().position().top,
                            'left' : self.button.position().left + self.button.offsetParent().position().left
                        };

						this.container.css({
							'top': buttonOffset.top + self.button.height(),
							'left': buttonOffset.left -(self.container.width() - self.button.width())
						});
					});

				},

				hideMenu: function() {
					this.button.toggleClass("open", false);
				
					this.container.hide();
					if (typeof this.resizeCallback == 'function') {
						$(window).unbind('resize', this.resizeCallback);
					}
					if (typeof this.modalCloseHandler == 'function') {
						$('body').unbind('click', this.modalCloseHandler);
					}
					this.resizeCallback = null;
				},

                toggleMenuEnabled: function(enabled) {
                    var self = this;
                    if(enabled) {
                        $(self.button).removeClass('disabled');
                    }
                    else {
                        $(self.button).addClass('disabled');
                    }
                },

				destroy : function() {
					$(this.button).off('click', this.buttonClickHandler);
					$(this.container).off('click', this.frequencySelectedHandler);
					this.frequencyButtonset.buttonset('destroy');

					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/time_period_range_selection_box.html',[],function () { return '<div class="time_selection_container" id="time_period_selection_box">\r\n  <h2>Change date range</h2>\r\n  <hr>\r\n  <div id="year-selector">\r\n    <div class="label">\r\n      Start year\r\n    </div>\r\n\t\t<div id="timeSliderContainer" class="range">\r\n\t\t\t<div class="labelContainer start"></div>\r\n\t\t\t<div class="labelContainer end"></div>\r\n\t\t\t<div class="timeSlider range"></div>\r\n\t\t</div>\r\n\t\t<div class="label">\r\n\t\t\tEnd year\r\n\t\t</div>\r\n  </div>\r\n  <div id="month-selector">\r\n\t\t<div class="label">\r\n\t\t\tStart month\r\n\t\t</div>\r\n\t\t<div id="start-month-buttons">\r\n\t\t\t<label for="start-january">JAN</label>\r\n\t\t\t<input name="start-month" id="start-january" type="radio" value="1">\r\n\t\t\t<label for="start-february">FEB</label>\r\n\t\t\t<input name="start-month" id="start-february" type="radio" value="2">\r\n\t\t\t<label for="start-march">MAR</label>\r\n\t\t\t<input name="start-month" id="start-march" type="radio" value="3">\r\n\t\t\t<label for="start-april">APR</label>\r\n\t\t\t<input name="start-month" id="start-april" type="radio" value="4">\r\n\t\t\t<br>\r\n\t\t\t<label for="start-may">MAY</label>\r\n\t\t\t<input name="start-month" id="start-may" type="radio" value="5">\r\n\t\t\t<label for="start-june">JUN</label>\r\n\t\t\t<input name="start-month" id="start-june" type="radio" value="6">\r\n\t\t\t<label for="start-july">JUL</label>\r\n\t\t\t<input name="start-month" id="start-july" type="radio" value="7">\r\n\t\t\t<label for="start-august">AUG</label>\r\n\t\t\t<input name="start-month" id="start-august" type="radio" value="8">\r\n\t\t\t<br>\r\n\t\t\t<label for="start-september">SEP</label>\r\n\t\t\t<input name="start-month" id="start-september" type="radio" value="9">\r\n\t\t\t<label for="start-october">OCT</label>\r\n\t\t\t<input name="start-month" id="start-october" type="radio" value="10">\r\n\t\t\t<label for="start-november">NOV</label>\r\n\t\t\t<input name="start-month" id="start-november" type="radio" value="11">\r\n\t\t\t<label for="start-december">DEC</label>\r\n\t\t\t<input name="start-month" id="start-december" type="radio" value="12">\r\n\t\t</div>\r\n\t\t<div id="end-month-buttons">\r\n\t\t\t<label for="end-january">JAN</label>\r\n\t\t\t<input name="end-month" id="end-january" type="radio" value="1">\r\n\t\t\t<label for="end-february">FEB</label>\r\n\t\t\t<input name="end-month" id="end-february" type="radio" value="2">\r\n\t\t\t<label for="end-march">MAR</label>\r\n\t\t\t<input name="end-month" id="end-march" type="radio" value="3">\r\n\t\t\t<label for="end-april">APR</label>\r\n\t\t\t<input name="end-month" id="end-april" type="radio" value="4">\r\n\t\t\t<br>\r\n\t\t\t<label for="end-may">MAY</label>\r\n\t\t\t<input name="end-month" id="end-may" type="radio" value="5">\r\n\t\t\t<label for="end-june">JUN</label>\r\n\t\t\t<input name="end-month" id="end-june" type="radio" value="6">\r\n\t\t\t<label for="end-july">JUL</label>\r\n\t\t\t<input name="end-month" id="end-july" type="radio" value="7">\r\n\t\t\t<label for="end-august">AUG</label>\r\n\t\t\t<input name="end-month" id="end-august" type="radio" value="8">\r\n\t\t\t<br>\r\n\t\t\t<label for="end-september">SEP</label>\r\n\t\t\t<input name="end-month" id="end-september" type="radio" value="9">\r\n\t\t\t<label for="end-october">OCT</label>\r\n\t\t\t<input name="end-month" id="end-october" type="radio" value="10">\r\n\t\t\t<label for="end-november">NOV</label>\r\n\t\t\t<input name="end-month" id="end-november" type="radio" value="11">\r\n\t\t\t<label for="end-december">DEC</label>\r\n\t\t\t<input name="end-month" id="end-december" type="radio" value="12">\r\n\t\t</div>\r\n\t\t<div class="label">\r\n\t\t\tEnd month\r\n\t\t</div>\r\n  </div>\r\n  <div id="quarter-selector">\r\n    <div class="label">\r\n      Start quarter\r\n    </div>\r\n\t\t<div id="start-quarter-buttons">\r\n\t\t\t<label for="start-first-quarter">1st</label>\r\n\t\t\t<input name="start-quarter" id="start-first-quarter" type="radio" value="1">\r\n\t\t\t<label for="start-second-quarter">2nd</label>\r\n\t\t\t<input name="start-quarter" id="start-second-quarter" type="radio" value="2">\r\n\t\t\t<label for="start-third-quarter">3rd</label>\r\n\t\t\t<input name="start-quarter" id="start-third-quarter" type="radio" value="3">\r\n\t\t\t<label for="start-fourth-quarter">4th</label>\r\n\t\t\t<input name="start-quarter" id="start-fourth-quarter" type="radio" value="4">\r\n\t\t</div>\r\n\t\t<div id="end-quarter-buttons">\r\n\t\t\t<label for="end-first-quarter">1st</label>\r\n\t\t\t<input name="end-quarter" id="end-first-quarter" type="radio" value="1">\r\n\t\t\t<label for="end-second-quarter">2nd</label>\r\n\t\t\t<input name="end-quarter" id="end-second-quarter" type="radio" value="2">\r\n\t\t\t<label for="end-third-quarter">3rd</label>\r\n\t\t\t<input name="end-quarter" id="end-third-quarter" type="radio" value="3">\r\n\t\t\t<label for="end-fourth-quarter">4th</label>\r\n\t\t\t<input name="end-quarter" id="end-fourth-quarter" type="radio" value="4">\r\n\t\t</div>\r\n\t\t<div class="label">\r\n\t\t\tEnd quarter\r\n\t\t</div>\r\n  </div>\r\n  <hr>\r\n  <div id="time-period-button-container">\r\n    <span id="time-period-submit">\r\n      <span>Submit</span>\r\n    </span>\r\n    <span id="time-period-cancel">\r\n      <span>Cancel</span>\r\n    </span>\r\n  </div>\r\n</div>\r\n';});

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('js/view/VC_Time_Period_Range_Selection_Button',[
  'classy',
	'text!templates/time_period_range_selection_box.html',
  'signals',
  'mustache',
  'simpleModal',
	'timeSlider'
],function(Class, timePeriodRangeBoxTemplate, signals) {
  return Class.$extend({

    __init__: function(container, config) {
      var self = this;

      this.container = container;
      this.button = $('#time-period-selection-button-container');
      this.timePeriodSelected = new signals.Signal();

			this.container.empty().append($(Mustache.to_html(timePeriodRangeBoxTemplate)));

			this.timeSlider = $('.timeSlider', this.container);
			this.timeSlider.slider({
				'orientation': 'horizontal',
				'range': true,
				'slide' : function(event, ui) {
					if(ui.values[0] >= ui.values[1]) {
						return false;
					}
					self.updateSliderLabel({ start : ui.values[0], end : ui.values[1] });
				},
				'change' : function(event, ui) {
					self.updateSliderLabel({ start : ui.values[0], end : ui.values[1] });
					self.toggleQuarterMonths({ start : ui.values[0], end : ui.values[1] });
				}
			});

			this.startQuarterButtonset = $('#start-quarter-buttons', this.container);
			this.startMonthButtonset = $('#start-month-buttons', this.container);
			this.endQuarterButtonset = $('#end-quarter-buttons', this.container);
			this.endMonthButtonset = $('#end-month-buttons', this.container);

			this.startQuarterButtonset.buttonset();
			this.startMonthButtonset.buttonset();
			this.endQuarterButtonset.buttonset();
			this.endMonthButtonset.buttonset();

			this.updateConfig(config, 'A');

			this.buttonClickHandler = function(e) {
				if (!(self.button.hasClass('disabled') || self.button.hasClass('open'))) {
					self.showMenu();
				}
			};
      this.button.on('click', this.buttonClickHandler);

			this.timePeriodSubmitHandler = function(){
				var params = { start : {}, end : {}};
				params.start.year = self.timeSlider.slider('values')[0];
				params.end.year = self.timeSlider.slider('values')[1];
				params.start.month = $('input:checked', self.startMonthButtonset).val();
				params.end.month = $('input:checked', self.endMonthButtonset).val();
				params.start.quarter = $('input:checked', self.startQuarterButtonset).val();
				params.end.quarter = $('input:checked', self.endQuarterButtonset).val();
				self.hideMenu();
				self.timePeriodSelected.dispatch(params);
			};
      $(this.container).on('click', '#time-period-submit', this.timePeriodSubmitHandler);

			this.timePeriodCloseHandler = function(){
				self.hideMenu();
			};
      $(this.container).on('click', '#time-period-cancel', this.timePeriodCloseHandler);

      this.modalCloseHandler = function(e) {
        if ($(e.target).parents('#time_dialog_container').length === 0) {
          self.hideMenu();
        }
      };
			
			this.resizeCallback = function() {
				var buttonOffset = self.button.position();
				$('#simplemodal-container').css({
					'top': buttonOffset.top,
					'left': buttonOffset.left - $('#time_dialog_container').width()
				});
			};
    },
		
		updateConfig : function(config, freq) {
			this.config = config;
			
			this.updateYearSliderStartEnd(freq);
		},

		updateYearSliderStartEnd : function(freq) {
			var min, max;

			switch(freq) {
				case 'A':
					min = parseInt(this.config.annualStart);
					max = parseInt(this.config.annualEnd);
					break;
				case 'M':
					min = parseInt(this.config.monthlyStart / 100);
					max = parseInt(this.config.monthlyEnd / 100);
					break;
				case 'Q':
					min = parseInt(this.config.quarterlyStart / 100);
					max = parseInt(this.config.quarterlyEnd / 100);
					break;
			}

			this.timeSlider.slider("option", "min", min);
			this.timeSlider.slider("option", "max", max);
		},

		toggleQuarterMonths : function(year) {
			var config = this.config;

			$.each($('input', this.startQuarterButtonset), function(index, input) {
				var datecode = (year.start * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode < config.quarterlyStart);
			});
			if($('input:checked', this.startQuarterButtonset).is(':disabled')) {
				$('input:enabled:first', this.startQuarterButtonset).prop('checked', true);
			}
			this.startQuarterButtonset.buttonset('refresh');

			$.each($('input', this.endQuarterButtonset), function(index, input) {
				var datecode = (year.end * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode > config.quarterlyEnd);
			});
			if($('input:checked', this.endQuarterButtonset).is(':disabled')) {
				$('input:enabled:last', this.endQuarterButtonset).prop('checked', true);
			}
			this.endQuarterButtonset.buttonset('refresh');

			$.each($('input', this.startMonthButtonset), function(index, input) {
				var datecode = (year.start * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode < config.monthlyStart);
			});
			if($('input:checked', this.startMonthButtonset).is(':disabled')) {
				$('input:enabled:first', this.startMonthButtonset).prop('checked', true);
			}
			this.startMonthButtonset.buttonset('refresh');

			$.each($('input', this.endMonthButtonset), function(index, input) {
				var datecode = (year.end * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode > config.monthlyEnd);
			});
			if($('input:checked', this.endMonthButtonset).is(':disabled')) {
				$('input:enabled:last', this.endMonthButtonset).prop('checked', true);
			}
			this.endMonthButtonset.buttonset('refresh');
		},
		
    updateButtonLabel : function(datecode, freq) {
      var textLabel = "";
			
      function dateToYear(y){
        return y.substring(0,4);
      }
      function dateToQuarter(q){
        var quarter = parseInt(q.substring(4,6))-1;
				var quarterNames = ["Q1", "Q2","Q3","Q4"];

				return quarterNames[quarter];
      }
      function dateToMonth(m){
        var month = parseInt(m.substring(4,6))-1;
				var monthNames = ["Jan","Feb","Mar","Apr","May","Jun", "Jul", "Aug","Sep","Oct","Nov","Dec"];

				return monthNames[month];
      }
      switch(freq) {
        case 'A':
          textLabel += datecode.start.toString() + ' - ' + datecode.end.toString();
          break;
        case 'M':
          textLabel += dateToMonth(datecode.start.toString()) + " " + dateToYear(datecode.start.toString()) + ' - ' + dateToMonth(datecode.end.toString()) + " " + dateToYear(datecode.end.toString());
          break;
        case 'Q':
					textLabel += dateToQuarter(datecode.start.toString()) + " " + dateToYear(datecode.start.toString()) + ' - ' + dateToQuarter(datecode.end.toString()) + " " + dateToYear(datecode.end.toString());
          break;
      }
      $('.text', this.button).text(textLabel);
    },

		updateSliderLabel : function(datecode) {
			var startLabelContainer = $('#timeSliderContainer .labelContainer.start', this.container);
			var endLabelContainer = $('#timeSliderContainer .labelContainer.end', this.container);

			startLabelContainer.text(datecode.start.toString().substring(0,4));
			endLabelContainer.text(datecode.end.toString().substring(0,4));
		},

		updateTimeSelections : function(datecode, freq) {
			this.datecode = datecode;
			this.freq = freq;

			this.updateButtonLabel(datecode, freq);
			this.updateYearSliderStartEnd(freq);

				var startYear = parseInt(datecode.start.toString().substring(0,4)),
						endYear = parseInt(datecode.end.toString().substring(0,4)),
						startMonth, endMonth,
						startQuarter, endQuarter;
			
				// set frequency class on container to show/hide month quarter selections
				switch(freq) {
					case 'A':
						$('#time_period_selection_box')
								.toggleClass('monthly quarterly', false)
								.toggleClass('annual', true);
						break;
					case 'M':
						$('#time_period_selection_box')
								.toggleClass('annual quarterly', false)
								.toggleClass('monthly', true);
						startMonth = parseInt(datecode.start.toString().substring(4, 6));
						startQuarter = Math.ceil(startMonth/3);
						endMonth = parseInt(datecode.end.toString().substring(4, 6));
						endQuarter = Math.ceil(endMonth/3);
						break;
					case 'Q':
						$('#time_period_selection_box')
								.toggleClass('monthly annual', false)
								.toggleClass('quarterly', true);
						startQuarter = parseInt(datecode.start.toString().substring(4, 6));
						startMonth = startQuarter * 3 - 2;
						endQuarter = parseInt(datecode.end.toString().substring(4, 6));
						endMonth = endQuarter * 3 - 2;
						break;
				}

			$('input[value=' + startQuarter + ']', this.startQuarterButtonset).prop('checked', true);
			$('input[value=' + endQuarter + ']', this.endQuarterButtonset).prop('checked', true);
			$('input[value=' + startMonth + ']', this.startMonthButtonset).prop('checked', true);
			$('input[value=' + endMonth + ']', this.endMonthButtonset).prop('checked', true);
			this.timeSlider.slider("values", [startYear, endYear]);
			this.updateSliderLabel(datecode);
		},

    showMenu: function() {
      var self = this;
			this.button.toggleClass("open", true);
      var buttonOffset = self.button.position();

      $('#time_dialog_container').css({
        'top': buttonOffset.top + self.button.height(),
				'left': buttonOffset.left + self.button.width() - $('#time_dialog_container').width(),
		  'z-index':'1200'
      }).show();

      // 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.
			setTimeout(function() {
				$('body').on('click', self.modalCloseHandler);
			}, 0);

      $(window).resize(this.resizeCallback = function() {
        var buttonOffset = self.button.position();
        $('#simplemodal-container').css({
          'top': buttonOffset.top + self.button.height(),
          'left': buttonOffset.left + self.button.width() - $('#time_dialog_container').width()
        });
      });

			
			$(window).on('resize', this.resizeCallback);

			this.updateSliderLabel(this.datecode);
    },

    hideMenu: function() {
			this.button.toggleClass("open", false);


			$('#time_dialog_container').hide();
			//reset selections
			this.updateTimeSelections(this.datecode, this.freq);

      if (typeof this.resizeCallback == 'function') {
        $(window).off('resize', this.resizeCallback);
      }
      if (typeof this.modalCloseHandler == 'function') {
        $('body').off('click', this.modalCloseHandler);
      }

      this.resizeCallback = null;
    },

		destroy : function() {
			this.button.off('click', this.buttonClickHandler);
			$(this.container).off('click', '#time-period-submit', this.timePeriodSubmitHandler);
			$(this.container).off('click', '#time-period-cancel', this.timePeriodCloseHandler);

			this.startQuarterButtonset.buttonset('destroy');
			this.startMonthButtonset.buttonset('destroy');
			this.endQuarterButtonset.buttonset('destroy');
			this.endMonthButtonset.buttonset('destroy');
			this.timeSlider.slider('destroy');

			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/analysisBox.html',[],function () { return '<div class="analysis_container">\r\n\t<h2>Related Analysis</h2>\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<h2>Electricity Monthly Update</h2>\r\n\tElectricity Monthly Update\r\n\t<h2>Additional Insight & Data</h2>\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\r\n\t\t\t\t<br/>\r\n\t\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\t<option class="link_prompt" value="">Jump To</option>\r\n\t\t\t\t\t<option class="link_option" value="/aer">Annual Energy Review</option>\r\n\t\t\t\t\t<option class="link_option" value="/totalenergy/data/monthly/">Monthly Energy Review</option>\r\n\t\t\t\t\t<option class="link_option" value="/electricity/annual/">Electric Power Annual</option>\r\n\t\t\t\t\t<option class="link_option" value="/cneaf/electricity/page/data.html">Detailed Databases</option>\r\n\t\t\t\t</select>\r\n\t\t\t</label>\r\n\t\t</form>\r\n\t\t<form id="projections" name="projections">\r\n\t\t\t<label for="p_data">Projections:\r\n\t\t\t\t<br/>\r\n\t\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\t<option class="link_prompt" value="">Jump To</option>\r\n\t\t\t\t\t<option class="link_option" value="/forecasts/aeo/">Annual Energy Outlook</option>\r\n\t\t\t\t\t<option class="link_option" value="/forecasts/steo/">Short-Term Energy Outlook</option>\r\n\t\t\t\t</select>\r\n\t\t\t</label>\r\n\t\t</form>\r\n\t</div>\r\n</div>\r\n\r\n';});

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();
			});

			this.modalCloseHandler = function(e) {
				if ($(e.target).parents('#analysis_dialog_container').length === 0) {
					self.hideDownload();
				}
			};
		},

		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;

			this.generateAnalysis(
				this.container,
				relatedAnalysis,
				moreAnalysisURL
			);

			$('html').css({
				overflow : 'hidden'
			});
			$('#mask').css({
					height: $(document).height(),
					width: $(document).width()
				})
				.show();

            var ad_wrap = $('#analysis_dialog_wrap');
            
			ad_wrap.show()
				.css({
                    top : $('header').height() + 15,
                    left : (ad_wrap.offsetParent().width() - ad_wrap.width()) / 2,
					'z-index': '50',
					maxHeight : '90vh'
			});

			// 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);
			});

			$(window).resize(this.resizeCallback = function() {
				$('#mask').css({
					height: $(document).height(),
					width: $(document).width()
				});
				$('#analysis_dialog_wrap').css({
                    top : $('header').height() + 15,
                    left : (ad_wrap.offsetParent().width() - ad_wrap.width()) / 2,
					maxHeight : '90vh',
					overflow: 'auto'
				});
			});
		},

		hideDownload: function() {
			$('#mask').hide();
			$('#analysis_dialog_wrap').hide();
			$('html').css({ overflow : 'auto' });

			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/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" />\r\n\t\t\t<label for="index0">None</label>\r\n\t\t\t<input type="radio" id="index1" name="index" value="1" />\r\n\t\t\t<label for="index1">Index to start as percent</label>\r\n\t\t\t<input type="radio" id="index2" name="index" value="2" />\r\n\t\t\t<label for="index2">Index to start as value</label>\r\n\t\t</div>\r\n\t</form>\r\n</div>\r\n';});

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 seriesNameOpts = "";
				 if(datum.DISPLAY_DATA_TYPE == 'generation' && datum.CHART_NAME )
				  {
				  		seriesNameOpts =  datum.CHART_NAME.split("-")[1].charAt(0).toUpperCase() + datum.CHART_NAME.split("-")[1].slice(1)+" - "+datum.DESCRIPTION;
				  }

				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 + ')' : ''),
					seriesName = seriesOpts.name = ((seriesNameOpts == "") ? ((usePinnedName ? datum.PINNED_NAME : datum.CHART_NAME) + (showUnits ? ' (' + datum.UNITS + ')' : '')) : seriesNameOpts),
					//seriesName = seriesOpts.name = datum.DESCRIPTION,

					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 (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) {
				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);
				};
				if (this.chart) {
					if (type == 'csv')
						this.chart.generateCSV();
					else {
						var options;
						switch (type) {
							case 'svg':
								options = {
									url: '/global/scripts/jquery/highcharts/exporting-server/index.php',
									type: 'image/svg+xml'
								};
								break;
							case 'pdf':
								options = {
									url: '/global/scripts/jquery/highcharts/exporting-server/index.php',
									type: 'application/pdf'
								};
								break;
							case 'png':
								break;
							default:
								options = {
									url: '/global/scripts/jquery/highcharts/exporting-server/index.php'
								};
								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) {
						// remove tooltip event to fix bug
						if (this.chart.pointer) {
							Highcharts.removeEvent(document, 'mousemove', this.chart.pointer._onDocumentMouseMove);
						}

						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" />\r\n\t\t\t\t<label for="columnValues0">Show values only</label>\r\n\t\t\t\t<input type="radio" id="columnValues1" name="columnvalues" value="1" />\r\n\t\t\t\t<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">\r\n\t\t\t\t<label for="columnEndpoints0">Graph first and last</label>\r\n\t\t\t\t<input type="radio" id="columnEndpoints1" name="columnendpoints" value="1">\r\n\t\t\t\t<label for="columnEndpoints1">Graph first</label>\r\n\t\t\t\t<input type="radio" id="columnEndpoints2" name="columnendpoints" value="2">\r\n\t\t\t\t<label for="columnEndpoints2">Graph last</label>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</form>\r\n</div>\r\n\r\n';});

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 seriesNameOpts = "";
			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) {
					if(d.DISPLAY_DATA_TYPE == 'generation' && d.CHART_NAME ){
						seriesNameOpts =  d.CHART_NAME.split("-")[1].charAt(0).toUpperCase() + d.CHART_NAME.split("-")[1].slice(1)+" - "+d.DESCRIPTION;
					}
					series.push({
						name: (seriesNameOpts == "" ? ((usePinnedName ? d.PINNED_NAME : d.CHART_NAME) + (yAxis.length > 1 ? ' (' + d.UNITS + ')' : '')) : seriesNameOpts) ,
						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':
										ret.push(!isNaN(startVal) ? startVal : null);
										break;
									case '2':
										ret.push(!isNaN(endVal) ? endVal : null);
										break;
									case '0':
										break;
									default:
										ret.push(!isNaN(startVal) ? startVal : null);
										ret.push(!isNaN(endVal) ? endVal : 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;
								default:
									ret.push(startVal);
									ret.push(endVal);
									break;
							}
						else
							ret.push(changePrefix + startVal + '-' + endVal);
						return ret;
					}(),
					min: function() {
						switch (values) {
							case "1":
								return 0;
							default:
								switch (endpoints) {
									case "1":
									case "2":
										return 0;
									default:
										return null;
								}
								return null;
						}

						return null;
					}(),
					max: function() {
						switch (values) {
							case "1":
								return 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] !== null) {
					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 'svg':
							options = {
								url: '/global/scripts/jquery/highcharts/exporting-server/index.php',
								type: 'image/svg+xml'
							};
							break;
						case 'pdf':
							options = {
								url: '/global/scripts/jquery/highcharts/exporting-server/index.php',
								type: 'application/pdf'
							};
							break;
						default:
							options = {
								url: '/global/scripts/jquery/highcharts/exporting-server/index.php'
							};
							break;
					}

					this.chart.exportChart(options);
				}
			}
		},

		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);
		},

		embed: function() {
			this.chart.openAPIEmbed();
		},

		destroy: function() {
			if (this.chart != null) {
				if (this.chart.hasRendered === true) {
					// remove tooltip event to fix bug
					if (this.chart.pointer) {
						Highcharts.removeEvent(document, 'mousemove', this.chart.pointer._onDocumentMouseMove);
					}

					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" class="map_controls_container">\r\n\t<h2>Map controls & legend</h2>\r\n\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> {{#mapTypes}}\r\n\t\t\t<input id="radio_{{id}}" type="radio" name="animated_map_type_radio" value="{{id}}" />\r\n\t\t\t<label for="radio_{{id}}">{{title}}</label>\r\n\t\t\t{{/mapTypes}}\r\n\t\t</div>\r\n\t\t<hr>\r\n\t\t<div id="animated_map_slider"></div>\r\n\t\t\r\n\t\t<div id="animated_map_transport_container" class="animation-controls">\r\n\t\t\t<input type="checkbox" id="play_button">\r\n\t\t\t<label for="play_button">Play</label>\r\n\t\t\t<input type="checkbox" id="pause_button">\r\n\t\t\t<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" class="map_units"></span>\r\n\t\t</div>\r\n\t\t<div id="map_legend"></div>\r\n\t</div>\r\n</div>\r\n\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()
				);
			};

		},

		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('&'));
		},

		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);
						},
						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;
		},

		__classvars__: {
			CONTINUOUS_MAP: '0',
			GROWTH_MAP: '1'
		}
	});
});

/**
 *	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 '<h2>{{title}}</h2>\n\n';});


define('text!templates/footnotes.html',[],function () { return 'NM = Not meaningful due to large relative standard error. {{#rseAvailable}}Show relative standard errors expressed as percent in parenthesis:\r\n<span><input type="checkbox" id="rse_checkbox"><label for="rse_checkbox"></label></span>{{/rseAvailable}}\r\n<br>\r\n<a class="lightbox" id="termsLink" href="data/termsAndDefs.php?topic={{topic}}&showDetail={{showDetail}}&showFilterValues={{showFilterValues}}">Terms and definitions</a>\r\n';});

define('js/pages/AggregateDataPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	//Facade
	'js/Facade',
	'js/Controller',
	//Model
	'js/model/aggregate/AggregateDataConfigProxy',
	'js/model/aggregate/AggregateDataRequestProxy',
	'js/model/aggregate/AggregateDataParameterProxy',
	'js/model/aggregate/AggregateDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/MapDataProxy',
	//View
	'js/view/MediatorElectricityTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorPinKeyVisualization',
	'js/view/MediatorViewStack',
	'js/view/MediatorLineChart',
	'js/view/MediatorColumnChart',
	'js/view/MediatorFrequencySelectionButton',
	'js/view/MediatorTimePeriodRangeSelectionButton',
	'js/view/MediatorHelpTooltip',
	'js/view/MediatorAnimatedMap',
	'js/view/MediatorAnalysis',
	'js/view/MediatorAggregateDataGrid',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_Frequency_Selection_Button',
	'js/view/VC_Time_Period_Range_Selection_Button',
	'js/view/VC_Analysis',
	'js/view/VC_LineChart',
	'js/view/VC_ColumnChart',
	'js/view/VC_AnimatedMap',
	'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,
	TopicSelectionMediator,
	FormMediator,
	DownloadMediator,
	VisualizationMediator,
	PinKeyVisualizationMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	FrequencySelectionButtonMediator,
	TimePeriodSelectionButtonMediator,
	HelpTooltipMediator,
	AnimatedMapMediator,
	AnalysisMediator,
	DataGridMediator,
	TopicPageLayout,
	FormDialog,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	FrequencySelectionButtonDialog,
	TimePeriodSelectionButtonDialog,
	AnalysisDialog,
	TopicLineChart,
	TopicColumnChart,
	EnerdatAnimatedMap,
	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/AggregateDataPage');

	////////////
	// 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.UNLOAD, ShutdownCommand);
		},

		__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 ShutdownCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand: function() {
			this.addSubCommand(RemoveViewCommand);
			this.addSubCommand(RemoveModelCommand);
		}
	});

	var RemoveViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeMediator(PageMediator.NAME);
		}
	});

	var RemoveModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeProxy(ConfigProxy.NAME);
			this.facade.removeProxy(RequestProxy.NAME);
			this.facade.removeProxy(ParameterProxy.NAME);
			this.facade.removeProxy(TableDataProxy.NAME);
			this.facade.removeProxy(HelpTooltipProxy.NAME);
			this.facade.removeProxy(MapDataProxy.NAME);
		}
	});

	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute: function(note) {
			var self = this;

			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();

			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
			this.facade.registerProxy(mapDataProxy);

			var tableParameter = {
				id: 'csv_table',
				type: 'csv',
				'class': 'ico_csv',
				target: 'table',
				label: 'Table (CSV)'
			};

			var downloadParams = $.extend({}, {
				'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
					]
				}]
			});

			configProxy.setVisualizations(VisualizationDialog.defaultConfig.items.slice());
			configProxy.setLinkTypes(VisualizationDialog.pinKeyConfig.items.slice());
			configProxy.setDownloadParameters(downloadParams);

		}
	});

	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]);
			}
        }
	});

	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),
					requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
					topic = configProxy.getTopic(requestProxy.getTopicId());

			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), { items: configProxy.getVisualizations() });
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var frequencySelectionButtonDialog = new FrequencySelectionButtonDialog(pageLayout.getFrequencyContainer());
			var timePeriodSelectionButtonDialog = new TimePeriodSelectionButtonDialog(pageLayout.getTimePeriodContainer(), topic);
			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 middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, false);
			var frequencySelectionButtonMediator = new FrequencySelectionButtonMediator(frequencySelectionButtonDialog);
			var timePeriodSelectionButtonMediator = new TimePeriodSelectionButtonMediator(timePeriodSelectionButtonDialog);
			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(middleTopicSelectionMediator);
			this.facade.registerMediator(frequencySelectionButtonMediator);
			this.facade.registerMediator(timePeriodSelectionButtonMediator);
			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);

			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, { rseAvailable: true, topic: requestProxy.getTopicId(), showFilterValues: true, showDetail: false }))
				.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,
                autoHeight : true,
                autoWidth : false,
                autoSize : false,
                fitToView : true,
                scrolling : 'auto',
                type : 'ajax'
			});

		},

		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 = 'Electricity 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:
					parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
					vc.getFootnoteContainer()
						.find('#rse_checkbox')
						.attr('checked', parameterProxy.getShowRSE());
					break;
				case AggregateDataFacade.TOPIC_PARAMS_UPDATED:
					var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
					var self = this;
					this.getViewComponent().getFootnoteContainer()
						.empty()
						.html(Mustache.to_html(footnoteTemplate, { rseAvailable: true, topic: requestProxy.getTopicId(), showFilterValues: true, showDetail: false }))
						.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,
                        autoHeight : true,
                        autoWidth : false,
                        autoSize : false,
                        fitToView : true,
                        scrolling : 'auto',
                        type : 'ajax'
                    });

					break;
				default:
					this.$super(note);
					break;
			}
		},

		onRemove : function() {

			this.facade.removeMediator(PinKeyVisualizationMediator.NAME);
			this.facade.removeMediator(AnalysisMediator.NAME);
			this.facade.removeMediator(AnimatedMapMediator.NAME);
			this.facade.removeMediator(HelpTooltipMediator.NAME);
			this.facade.removeMediator(ColumnChartMediator.NAME);
			this.facade.removeMediator(LineChartMediator.NAME);
			this.facade.removeMediator(ViewStackMediator.NAME);
			this.facade.removeMediator(DataGridMediator.NAME);
			this.facade.removeMediator(TimePeriodSelectionButtonMediator.NAME);
			this.facade.removeMediator(FrequencySelectionButtonMediator.NAME);
			this.facade.removeMediator(TopicSelectionMediator.NAME);
			this.facade.removeMediator(VisualizationMediator.NAME);
			this.facade.removeMediator(DownloadMediator.NAME);
			this.facade.removeMediator(FormMediator.NAME);
		},

		__classvars__: {
			NAME: 'PageMediator'
		}

	});


	/////////////////////
	// View Components //
	/////////////////////

	var PageLayout = TopicPageLayout.$extend({

		__init__: function() {
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts': {
					'selected': true,
					'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'
				},
				'mainAnalysisButtonOpts': {
					'selected': false,
					'id': 'main_analysis_button'
				},
				'overlayAnalysisButtonOpts': {
					'selected': true,
					'id': 'overlay_analysis_button'
				},
				showTabs : false,
				showSearch : false
			});
		}
	});

	return page;
});

define('js/model/mixins/ConfigMixinTabs',[], function() {
    return {
        getTabOptions: function() {
            return this.getData().tabOptions;
        },
        setTabOptions: function(tabOptions) {
            this.getData().tabOptions = tabOptions;
        }
    };
});

define('js/model/plantlist/ListConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/mixins/ConfigMixinDownload',
    'js/model/mixins/ConfigMixinTabs'
], function(
	ConfigProxy,
	ConfigMixinDownload,
    ConfigMixinTabs
) {
	return ConfigProxy.$extend({
		__include__: [
			ConfigMixinDownload,
            ConfigMixinTabs
		]
	});
});

define('js/model/plantlist/ListRequestProxy',[
	'js/model/RequestProxy',
	'js/model/mixins/RequestMixinTopic'
], function(
	RequestProxy,
	RequestMixinTopic
) {
	return RequestProxy.$extend({
		__include__: [
			RequestMixinTopic
		]
	});
});

define('js/model/mixins/ParameterMixinDateCode',[
    'js/Facade'
], function(
    Facade
    ) {
	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;
		},

        getInitialDateCode: function(freq) {
            switch (freq) {
                case 'A': // Annual
                    return parseInt(this.getCurrentYear());
                default: // Monthly, Quarterly
                    return parseInt(this.getCurrentYear()+'01');
            }
        },

        getCurrentYear: function() {
            return this.getTimeParams().datecode.toString().slice(0,4);
        }
	};
});

define('js/model/mixins/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/mixins/ParameterMixinTab',[], function() {
	return {
		getTab: function() {
			return this.getQuery().tab;
		},

		validateTab: function(def, topic, validTabs) {
			this.checkVal(def, 'tab', validTabs[0], function(val) {
				return validTabs.indexOf(val) > -1;
			});
		},

		resetTab: function() {
			delete this.getQuery().tab;
		}
	};
});

define('js/model/mixins/ParameterMixinPlantCodes',[], function() {
    return {

        // toggle plant compare selection

        getShowPlantCompare: function() {
            return this.getQuery().compare;
        },
        setShowPlantCompare: function(showPlantCompare) {
            this.getQuery().compare = showPlantCompare;
        },
        resetShowPlantCompare: function() {
            delete this.getQuery().compare;
        },

        // compare plants selected
        
        hasPlants: function() {
            return this.getQuery().plants != null;
        },
        findPlant: function(plant) {
            return this.getPlants().find( function( ele ) {
                return ele.code === plant.code;
            });
        },
        plantExistsByCode: function(plantCode) {
            return this.getPlantCodes().indexOf(plantCode) > -1;
        },
        getPlantCodes: function() {
            return this.getPlants().map(function(p) {
                return p.code;
            });
        },
        plantExists: function(plant) {
            return this.findPlant(plant) != null;
        },
        getPlants: function() {
            return this.hasPlants() ? this.removeEmpty(this.getQuery().plants) : [];
        },
        setPlants: function(plants) {
            this.getQuery().plants = this.removeEmpty(plants);
        },
        addPlant: function(plant) {
            if(!this.plantExists(plant)) {
                var plants = this.getPlants();
                plants.push(plant);
                this.setPlants(plants);
            }
        },
        removePlant: function(plant) {
            var plants = this.getPlants().filter(function (p) {
                return p.code !== plant.code;
            });
            this.setPlants(plants);
        },
        getCountSelectPlants: function() {
            return this.getPlants().length;
        },
        resetPlants: function() {
            delete this.getQuery().plants;
        },
        removeEmpty: function(plants) {
            return plants.filter(function(n){
                return n != undefined;
            });
        },
        maxSelected: function() {
            return this.getCountSelectPlants() >= 4;
        }
    };
});

define('js/model/plantlist/ListParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/mixins/ParameterMixinTopic',
	'js/model/mixins/ParameterMixinReport',
	'js/model/mixins/ParameterMixinFrequency',
	'js/model/mixins/ParameterMixinAggColumn',
	'js/model/mixins/ParameterMixinDateCode',
	'js/model/mixins/ParameterMixinDateCodes',
	'js/model/mixins/ParameterMixinGetCharted',
	'js/model/mixins/ParameterMixinTab',
    'js/model/mixins/ParameterMixinPlantCodes'
], function(
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	ParameterMixinTopic,
	ParameterMixinReport,
	ParameterMixinFrequency,
	ParameterMixinAggColumn,
	ParameterMixinDateCode,
	ParameterMixinDateCodes,
	ParameterMixinGetCharted,
	ParameterMixinTab,
    ParameterMixinPlantCodes
) {
	return ParameterProxy.$extend({

		__include__: [
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinAggColumn,
			ParameterMixinDateCode,
			ParameterMixinDateCodes,
			ParameterMixinGetCharted,
			ParameterMixinTab,
            ParameterMixinPlantCodes
		],

		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',
				'tab',
                'plants'
			]);
		},

		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(),
				'tab' : this.getTab()
			}, 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;
				if (this.getQuery()[aggColumn] !== undefined) {
					ret += '&' + aggColumn + '=' + this.getQuery()[aggColumn];
				}
			}

			ret += '&freq=' + this.getFreq();
			ret += '&datecode=' + this.getDatecode();
			ret += '&tab=' + this.getTab();
			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(),
                frequencyParamsUpdated = !('freq' in requestProxy.getQuery()),
                tabParamsUpdated = !('tab' in requestProxy.getQuery()),
                timeParamsUpdated = !('datecode' in requestProxy.getQuery());

            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 |= timeParamsUpdated |= tmpKey == 'datecode';
                // Check and see if our frequency has changed
                frequencyParamsUpdated |= tmpKey == 'freq';
                // Check and see if our agg column order has changed
                tableParamsUpdated |= tmpKey == 'agg';
                // Check and see if our tab  has changed
                tabParamsUpdated |= tmpKey == 'tab';



                // 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;
                }
            });

            tableParamsUpdated |= tabParamsUpdated || frequencyParamsUpdated;

            this.updateDateCodes(this.getFreq(), tmpTopic);

            this.updateReportIndex();

            if(tabParamsUpdated) {
                this.sendNotification(Facade.TAB_PARAMS_UPDATED);
			}
            if(timeParamsUpdated || frequencyParamsUpdated) {
                this.sendNotification(Facade.TIME_PARAMS_UPDATED);
            }
            if(frequencyParamsUpdated) {
                this.sendNotification(Facade.FREQUENCY_PARAM_UPDATED);
            }
            if (tableParamsUpdated)
                this.sendNotification(Facade.TABLE_PARAMS_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);

			var tabs = [];

			$('input[name="tabs"]').each(function() {
				var tabValue = $(this).val();
				switch(tabValue) {
					case 'water':
						$('input[name="water"]').each(function() {
							tabs.push(tabValue + '-' + $(this).val());
						});
						break;
					case 'consumption':
						$('input[name="consumption"]').each(function() {
							tabs.push(tabValue + '-' + $(this).val());
						});
						break;
					default:
						tabs.push(tabValue);
						break;
				}
			});


			this.validateTab(vals, topic, tabs);
		},

        togglePlantCompare: function() {
            if(this.getShowPlantCompare()) {
                this.resetShowPlantCompare();
                this.resetPlants();
            }
            else {
                this.setShowPlantCompare(true);
                this.resetPlants();
            }
        },

        toggleShowPlantCompare: function() {
            this.resetShowPlantCompare();
            this.resetPlants();
        },

        toggleHidePlantCompare: function() {
            this.setShowPlantCompare(true);
            this.resetPlants();
        },

        updatePlantCompareSelection: function(input) {
            if(input.checked) {
                this.addPlant(input);
            }
            else {
                this.removePlant(input);
            }
        },

        getPlantCodeString: function(input) {
            return this.getPlantCodes().join('/');
        },

        enableFrequency: function() {
            return !(this.getTab() == "annual_emissions" && this.getFreq() == "A");
        }
	});
});

define('js/view/MediatorPlantListMap',[
	'puremvc',
	'js/Facade',
	'js/model/TableDataProxy',
	'js/model/ParameterProxy',
	'us-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.POWER_PLANT_HIGHLIGHT,
				Facade.DOWNLOAD_OPTION_SELECTED

			]);
		},

		handleNotification: function(note) {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
				parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
				self = this,
				vc = this.getViewComponent(),
				titleArray = tableDataProxy.getMapTitle().split(','),

			//Only displaying the first 5 filters
				tempTitle = titleArray.slice(0,5);

			//Adding ellipses if more that 5 filters are selected
			if(titleArray.length > 5){
				tempTitle.push('...');
			}
			var mapTitle = tempTitle.join(', ');

			//Adds title attribute with full list of filters when user hovers over map title.
			vc.titleContainer.attr('title', titleArray.join(', '));

			switch (note.getName()) {
				case Facade.TABLEDATA_UPDATED:
					if (tableDataProxy.hasData()) {
						if (!tableDataProxy.getData().SINGLE_STATE) {
							vc.mapContainer.addClass('clickable-regions ').removeClass('clickable-markers');
							vc.update(
								tableDataProxy.getCounts(),
								mapTitle, {
									'zoomOnScroll': true,
									'map': 'us_merc_en',
									'onRegionLabelShow': function(event, label, code) {
										self.onRegionLabelShow(event, label, code);
									},
									'regionStyle': {
										hover: {
											stroke: '#FFFF96',
											"fill-opacity": 1,
											"stroke-width": 3,
											"stroke-opacity": 1
										}
									},
									'onMarkerLabelShow': null,
									'markers': null,
									'legendTitle': self.getLegendTitle()
								}
							);
						} else {
							vc.mapContainer.addClass('clickable-markers').removeClass('clickable-regions ');
							var stateCounts = tableDataProxy.getData().STATE_COUNTS, stateKey;
							$.each(stateCounts, function(key, value) {
								if(value > 0) {
									stateKey = key.split('-')[1].toLowerCase();
									return false;
								}
								return true;
							});
							require(['us' + stateKey + '-merc-en'], function() {
								vc.update(
									tableDataProxy.getCounts(),
									mapTitle, {
										'zoomOnScroll': true,
										'zoomMax': 110,
										'map': 'us' + stateKey + '_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.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 (typeof elem !== 'undefined' && elem !== null) {
						if (body.highlight) {
							elem.applyAttr('stroke', '#FFFF96');
							elem.applyAttr('stroke-width', elem.properties['stroke-width'] * 2);
						} else {
							elem.applyAttr('stroke', elem.properties.stroke);
							elem.applyAttr('stroke-width', elem.properties['stroke-width']);
						}
					}
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if (note.body.target == 'map' && (note.body.type == 'png' || note.body.type == 'pdf')) {
						this.getViewComponent().exportMap(note.body.type);
					} else if (note.body.target == 'map' && (note.body.type == '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;
				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>' +
				'Plant code: ' + value.code + '<BR>' +
				'Sector: ' + value.sector + '<BR>' +
				'Fuel types: ' + value.fuel + '<BR>' +
				'Prime movers: ' + value.primemover + '<BR>' +
				'Location: ' + value.latLng[0] + ', ' + value.latLng[1];
			$(label).html(labelTxt);
			return labelTxt;
		},

		onMarkerOver: function(event, code) {
			var vc = this.getViewComponent();

			this.sendNotification(Facade.POWER_PLANT_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.POWER_PLANT_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];
			if (code) {
				$(label).html($(label).html() + ' ' + (value !== void 0 ? value : 0) + ' plants');
			} else {
				label.empty();
			}
		},

		getLegendTitle: function() {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			return tableDataProxy.getMarkers().length + ' total power plants';
		},

		getMarkers: function() {
			var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var vc = this.getViewComponent();
			var ret = $.merge([], tableDataProxy.getMarkers());

			$.each(ret, function(index, value) {
				vc.$class.styleMarkerPlant(value);
			});

			return ret;
		},

		__classvars__: {
			NAME: 'ListMapMediator'
		}
	});
});

define('js/model/plantlist/ListPlantTableDataProxy',[
	'js/Facade',
    'js/model/ConfigProxy',
    'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/view/MediatorPlantListMap',
    'js/view/FilterSerializers',
	'slickdataview'
], function(
	Facade,
    ConfigProxy,
    ParameterProxy,
	TableDataProxy,
	ListMapMediator,
    FilterSerializers
) {
	return TableDataProxy.$extend({

		isUpdating: false,

		__init__: function() {
			this.$super(this.$class.NAME);
			this.setData('SINGLE_STATE', false);
		},


        createTitle : function() {
            var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
                parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                topic = configProxy.getTopic(parameterProxy.getTopicId()),
                i,
                titleArray = [];

            var agg = parameterProxy.getAgg(),
                aggArray = agg.split(',');

            if(!agg){
                return null;
            }


            function getAggColumn(id) {
                var i;

                for(i = 0; i < topic.aggColumns.length; i++) {
                    if(topic.aggColumns[i].aggColumnId == id) {
                        return topic.aggColumns[i];
                    }
                }

                return null;
            }

            function getFilterValue(aggColumn, filterValueId) {
                var i;

                for(i = 0; i < aggColumn.filterGroup.filterValues.length; i++) {
                    if(aggColumn.filterGroup.filterValues[i].valueId == filterValueId) {
                        return aggColumn.filterGroup.filterValues[i];
                    }
                }

                return null;
            }

            for(i = 0; i < aggArray.length; i++ ){
                var aggColumn = getAggColumn(aggArray[i]),
                    queryParams = FilterSerializers.deserializeFilter(parameterProxy.getQuery()[aggColumn.filterGroup.varName]),
                    sector = false,
                    filterIndex0 = getFilterValue(aggColumn, queryParams[0]),
                    filterIndex1 = getFilterValue(aggColumn, queryParams[1]),
                    filterIndexSecLast = getFilterValue(aggColumn, queryParams[queryParams.length-2]),
                    filterIndexLast = getFilterValue(aggColumn, queryParams[queryParams.length-1]);

                if(aggColumn.filterGroup.varName == 'sec'){
                    sector = true;
                }

                //Sector data constructs parent/child relationship with the largest value in array as being the parent. Opposite of states filter.
                //Logic for SECTOR is checked in reverse comparing last index valueId to the first and second to last index's parentValueId's

                     //Checking if more that one filter is selected
                    //Title will display the parent description if one parent is selected
                    //If only children are selected, if multiple parents are selected,
                    // or if a parent is selected along with children that are not related then the 'multiple' phrase will be displayed.
                if(sector) {
                    if(queryParams.length > 1){
                        if (filterIndexLast.hasChildren == null || filterIndexSecLast.parentValueId != filterIndexLast.valueId || filterIndex0.parentValueId != filterIndexLast.valueId || queryParams.length > 5) {
                            titleArray.push('multiple ' + aggColumn.description + 's');
                        }
                        else {
                            titleArray.push(filterIndexLast.description);
                        }
                    }
                    else if (queryParams.length == 1) {
                        titleArray.push(filterIndexLast.description);
                    }
                }
                else{
                    //For States filter, Parent valueId's will always be lower value's than children valueId's. Opposite of Sector filter.
                    //Logic here is based on comparing first index valueId to the second and last index's parentValueId's
                    //Same conditionals as before but for all other filters that are not Sector.
                    if (queryParams.length > 1) {
                        if (filterIndex0.hasChildren == null || filterIndex1.parentValueId != filterIndex0.valueId || filterIndexLast.parentValueId != filterIndex0.valueId ) {
                            titleArray.push('multiple ' + aggColumn.description + 's');
                        }
                        else{
                            titleArray.push(filterIndex0.description);
                        }
                    }

                    else if (queryParams.length == 1) {
                        titleArray.push(filterIndex0.description);
                    }
                }
                sector = false;
            }

            return titleArray;
        },


		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.hasGroupedColumns() ? self.listFilter : self.listFilterByGroup);
							dataView.setItems(tableData.DATA);

                            tableData.DESCRIPTION = self.createTitle();


							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;
								}())
							}));
							self.isUpdating = false;
							self.sendNotification(Facade.TABLEDATA_UPDATED);
						}
					}
				);
			}
		},

        getTableDataStartEndDates: function() {
            var data = this.getFirstDataOccurrence();
            switch(data.frequency) {
                case 'A':
                    config["annualStart"] = data.min_datecode;
                    config["annualEnd"] = data.max_datecode;
                    break;
                case 'M':
                    config["monthlyStart"] = data.min_datecode;
                    config["monthlyEnd"] = data.max_datecode;
                    break;
                case 'Q':
                    config["quarterlyStart"] = data.min_datecode;
                    config["quarterlyEnd"] = data.max_datecode;
                    break;
            }
            return config;
        },

		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) {
                                value = item[field].toString().toUpperCase();
                                found = ~value.indexOf(token);
                                if(found) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return found;
        },

        listFilterByGroup: function(item, args) {
            var found = true,
                field, value, tokens, token, i, j;

            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) {
                                value = item[field].toString().toUpperCase();
                                found = ~value.indexOf(token);
                                if(found) {
                                    break;
                                }
                                // Check if this is a plant Row
                                else if(item.plant_code !== undefined) { // If any children are filtered, force add the parent row
                                    for (j = item.id+1; j < args.dataView.getItems().length; j++) {
                                        var childItem = args.dataView.getItemById(j);
                                        var isChildItem = childItem.plant_code === undefined && childItem.group_by_column.toString().toUpperCase() == item.plant_name.toString().toUpperCase();
                                        found = isChildItem && args.fnFilter(childItem, args);
                                        if(found) {
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return found;
        },

		updateListFilter: function(searchString) {
			if (this.getTableData()) {
				this.getTableData().setFilterArgs({
					'searchString': searchString,
                    'dataView': this.getTableData(),
                    'fnFilter': this.listFilter
                });
                this.getTableData().refresh();
			}
		},

        hasGroupedColumns: function() {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            switch (parameterProxy.getTab()) {
                case "water-rate":
                case "water-source":
                    return true;
                default:
                    return false;
            }
        },

		getDataColumns: function() {
			return this.getData().DATA_COLUMNS;
		},

		getTableData: function() {
			return this.getData().dataView;
		},

        updatePlantSelectData: function(plant) {
            var data = this.getTableData();
            var dataItem = this.getPlantSelectDataItem(plant);
            dataItem.checked = plant.checked;
            data.updateItem(dataItem.id,dataItem);
        },

        getPlantSelectDataItem: function(plant) {
            return this.getTableData().getItemById(plant.id);
        },

        resetPlantSelectData: function(plants) {
            var self = this;
            var data = this.getTableData();
            var dataItem;
            $.each(plants, function(index, plant) {
                dataItem = self.getPlantSelectDataItem(plant);
                dataItem.checked = false;
                data.updateItem(dataItem.id,dataItem);
            });
        },

		getTableTitle: function() {
            return this.getData().DESCRIPTION != null ? "Plants for " + this.getData().DESCRIPTION : "All plants";
		},

		getMapTitle: function() {
            return this.getData().DESCRIPTION != null ? "Number of plants for " + this.getData().DESCRIPTION : "Number of plants";
		},

        getFirstDataOccurrence: function() {
            return this.getData().FIRST_DATA_OCCURRANCE;
        },

        getNoDataMessage: function(tab) {
            var firstDataOccurrence = this.getFirstDataOccurrence(),
                freq = firstDataOccurrence.frequency,
                minDateCode = firstDataOccurrence.min_datecode,
                maxDateCode = firstDataOccurrence.max_datecode,
                tabTitle = this.getTabTitle(tab),
                dateText = this.getFreqTextFromDateCode(freq,minDateCode, maxDateCode);

            return "Information for <i><strong>"+tabTitle+"</strong></i> is only available " + dateText + ".";
        },

        getTabTitle: function(tab) {
            switch (tab) {
                case "generation":
                    return 'Generation';
                case "consumption-quantity":
                case "consumption-mmbtu_unit":
                case "consumption-mmbtu":
                    return 'Consumption';
                case "water-rate":
                case "water-volume":
                case "water-intensity":
                case "water-source":
                    return 'Water';
                case "annual_emissions":
                    return 'Annual emissions';
                default:
                    return 'Overview';
            }
        },

        getTableSubTitle: function(tab) {
            switch (tab) {
                case "generation":
                    return 'Net generation in megawatthours';
                case "consumption-quantity":
                    return 'Consumption by quantity';
                case "consumption-mmbtu_unit":
                    return 'Consumption by MMBtu  per unit';
                case "consumption-mmbtu":
                    return 'Consumption by MMBtu';
                default:
                    return null;
            }
        },

        getFreqTextFromDateCode: function(freq,minDateCode, maxDateCode) {
            var params = [], parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            params.freq = freq;
            params.datecode = minDateCode;
            var minLink = parameterProxy.getFullPath(params);
            params.datecode = maxDateCode;
            var maxLink = parameterProxy.getFullPath(params);
            switch (freq) {
                case 'A':
                    return "<strong>Annually</strong> from <strong><a class='nav-no-data'  href='./#/" + minLink + "'>" + this.getYearString(minDateCode) + "</a></strong> to <strong><a class='nav-no-data'  href='./#/" + maxLink + "'>" + this.getYearString(maxDateCode) + "</a></strong>";
                case 'Q':
                    return "<strong>Quarterly</strong> from <strong><a class='nav-no-data'  href='./#/" + minLink + "'>" + this.getQuarterString(minDateCode)+" Quarter of "+this.getYearString(minDateCode) + "</a></strong> to <strong><a class='nav-no-data'  href='./#/" + maxLink + "'>" + this.getQuarterString(maxDateCode)+" Quarter of "+this.getYearString(maxDateCode) + "</a></strong>";
                case 'M':
                    return "<strong>Monthly</strong> from <strong><a class='nav-no-data'  href='./#/" + minLink + "'>" + this.getMonthString(minDateCode)+" of "+this.getYearString(minDateCode) + "</a></strong> to <strong><a class='nav-no-data'  href='./#/" + maxLink + "'>" + this.getMonthString(maxDateCode)+" of "+this.getYearString(maxDateCode) + "</a></strong>";
            }
        },

        getYearString: function(dateCode) {
            return dateCode.slice(0,4);
        },

        getMonthString: function(dateCode) {
            var monthDayString = dateCode.slice(4,6);
            var months = [
                'January', 'February', 'March', 'April', 'May',
                'June', 'July', 'August', 'September',
                'October', 'November', 'December'
            ];
            return months[monthDayString - 1] || '';
        },
        getQuarterString: function(dateCode) {
            var monthDayString = dateCode.slice(5,6);
            var quarters = [
                '1st', '2nd', '3rd', '4th'
            ];
            return quarters[monthDayString - 1] || '';
        },

		getCounts: function() {
			return this.getData().mappedCounts;
		},

		getMarkers: function() {
			var data = this.getData().MARKERS;
			var ret = [];
			var marker = this.facade.retrieveMediator(ListMapMediator.NAME).getViewComponent().$class.MARKER;

			$.each(data, function(index, value) {
				var tmp = {
					'latLng': [value.lat, value.lon],
					'name': value.plant_name,
					'code': value.plant_code,
					'fuel': value.fuel_types,
					'sector': value.sector_name,
					'primemover': value.prime_movers,
					'primaryFuel': value.primary_fuel
				};

				var fuelType = value.primary_fuel;

				if (marker.hasOwnProperty(fuelType)) {
					tmp.style = {
						fill: marker[value.primary_fuel].color
					};
				}

				ret.push(tmp);
			});

			return ret;
		},

		hasData: function() {
			if (typeof this.getTableData() != 'undefined') {
				return true;
			}
			return false;
		},

        hasValidDateBounds: function(datecode) {
            var dateBounds = this.getFirstDataOccurrence(),
                minDateCode = dateBounds.min_datecode,
                maxDateCode = dateBounds.max_datecode;
            return datecode >= minDateCode && datecode <= maxDateCode;
        },

		__classvars__: {
			PROXY_URL: 'data/index.php',
			PROXY_METHOD: 'getPlantListTabs'
		}
	});
});


define('text!templates/checkbox_item.html',[],function () { return '{{#visible}}\r\n<div class="grid-checkbox-column {{cssClass}}">\r\n    <input id="{{id}}" type="checkbox" {{#checked}} checked {{/checked}} {{#disabled}} disabled {{/disabled}}><label for="{{id}}"></label>\r\n</div>\r\n{{/visible}}';});


if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
        value: function (predicate) {

            if (this == null) {
                throw new TypeError('this is null or not defined');
            }

            var obj = Object(this);
            var len = obj.length >>> 0;

            if (typeof predicate !== 'function') {
                throw new TypeError('predicate must be a function');
            }

            var thisArg = arguments[1];

            var index = 0;

            while (index < len) {
                var iValue = obj[index];
                if (predicate.call(thisArg, iValue, index, obj)) {
                    return iValue;
                }
                index++;
            }

            return undefined;
        }
    });
};
define("ie-array-find-polyfill", function(){});

define('js/view/MediatorPlantListDataGrid',[
    'puremvc',
    'js/Facade',
    'js/model/ConfigProxy',
    'js/model/ParameterProxy',
    'js/view/GridFormatters',
    'js/model/TableDataProxy',
    'text!templates/checkbox_item.html',
    'ie-array-find-polyfill'
], function(
    puremvc,
    Facade,
    ConfigProxy,
    ParameterProxy,
    GridFormatters,
    TableDataProxy,
    CheckboxItemTemplate
    ) {
    return puremvc.Mediator.$extend({

        __init__: function(viewComponent) {
            this.$super(this.$class.NAME, viewComponent);
        },
        onRegister: function() {
            var self = this;

            $(this.getViewComponent().container).on('click', '.update-filter-button', function(event) {
                $('#main_filter_button').click();
                event.stopPropagation();
            });

            this.getViewComponent().onClick.add(function(row, cell, datum, event) {
                var vc = self.getViewComponent(),body;
                var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
                var eventType = event.target.type;
                var plant_code = datum.plant_code;

                if(parameterProxy.getShowPlantCompare() && plant_code !== undefined) {
                    var checkbox = $(vc.container.find('#'+plant_code+':checkbox'));
                    var requiresToggle = (eventType != 'checkbox');
                    var disabled = checkbox.prop('disabled');
                    var checked = checkbox.prop('checked');

                    if(checkbox === undefined || (requiresToggle && disabled)) { // for row click
                        return;
                    }
                    body = {
                        id: datum.id,
                        code: plant_code,
                        name: datum.plant_name,
                        checked: requiresToggle ? !checked : checked      ,
												latLng : [parseFloat(datum.lat), parseFloat(datum.lon)]
                    };
                    self.sendNotification(Facade.PLANT_COMPARE_SELECTION_CLICKED, body);
                }
                else if (event.target.className == 'link_cell' && plant_code != null) {
                    var freq = self.facade.retrieveProxy(ParameterProxy.NAME).getTimeParams().freq;
                    var label = 'plant/' + plant_code + '?freq=' + freq;

                    body = {
                        label: label,
                        code: plant_code,
                        freq: freq
                    };
                    self.sendNotification(Facade.PLANT_LIST_CLICK_THROUGH, body, 'PlantListClickThrough');
                }
            });
        },

        listNotificationInterests: function() {
            return this.$super().concat([
                Facade.TABLEDATA_UPDATED,
                Facade.DOWNLOAD_OPTION_SELECTED,
                Facade.POWER_PLANT_HIGHLIGHT,
                Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED,
                Facade.PLANT_COMPARE_SELECTION_UPDATED
            ]);
        },

        handleNotification: function(note) {
            var self = this;
            var body = note.getBody();
            var vc = this.getViewComponent();
            switch (note.getName()) {
                case Facade.TABLEDATA_UPDATED:
                case Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED:
                case Facade.PLANT_COMPARE_SELECTION_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();

                    var columns = this.generateColumns(
                        tableDataProxy.getDataColumns(),
                        data,
                        timeParams.freq,
                        parameterProxy.getShowPlantCompare(),
                        parameterProxy.maxSelected()
                    );
                    var options = {
                        'frozenColumn' : -1
                    };
                    var plantNameColumn = parameterProxy.getShowPlantCompare() ? 1 : 0;
                    if(parameterProxy.getTab() !== 'overview' && parameterProxy.getTab() !== 'annual_emissions') {
                        options.frozenColumn = plantNameColumn;
                    }
                    columns[plantNameColumn].width = 200;
                    vc.destroy();
                    vc.slickgrid = null;

                    if(data.getLength() != 0) {
                        vc.update(data, columns, options);
                        vc.slickgrid.setSortColumn(columns[0].id, true);
                        // Custom sort for columns grouped by plant
                        this.configureSort();
                        $.each($('.slick-sort-indicator'), function(index, elem) {
                            $(elem).insertBefore($(elem).prev());
                        });
                        $('.slick-sort-indicator').attr('title', 'Click to sort');
                        this.addSubTitle();
                        this.sendNotification(Facade.TABLE_RENDERED);
                    }
                    else { // No Data
                        var dataBounds = tableDataProxy.getFirstDataOccurrence(),
                            tab = parameterProxy.getTab()
                            var message;
                        message = "<h2 class='no-data-message'>No data for the selected filter <span>(<span class='update-filter-button'>UPDATE FILTER</span>)</span></h2>";
                        vc.innerGridContainer.append(message);
                        this.removeSubTitle();

                        if(!tableDataProxy.hasValidDateBounds(timeParams.datecode)) { // No data for these time params
                            // Mask the page with a message & auto-redirect to latest available year
                            var container = $('#pagecontent');
                            body = {
                                freq: dataBounds.frequency,
                                datecode: dataBounds.max_datecode,
                                tab: tab
                            };
                            message = tableDataProxy.getNoDataMessage(tab);
                            container.mask('<span class="loading">' + message + '<br/>Redirecting to latest...' + '</span>');
                            container.delay(5000).queue(function() {
                                self.sendNotification(Facade.AUTO_REDIRECT, body);
                                $(this).dequeue();
                            });
                            $(this).dequeue();
                        }
                        else {
                            this.sendNotification(Facade.TABLE_RENDERED);
                        }

                    }
                    break;
                case Facade.DOWNLOAD_OPTION_SELECTED:
                    if (note.body.target == 'table' && note.body.type == 'csv') {
                        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.POWER_PLANT_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, showPlantCompare, maxSelected) {
            var self = this;
            var columns = [];

            // default & overridden column configurations
            var defaultColumn = {'id': 'default','data_type':'number','cssClass': 'slick-header-columns-right','headerCssClass':'slick-header-columns-right'};
            var overrides = [
                {'id': 'plant_name','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'plant_code','data_type':'number','cssClass':null,'headerCssClass':null},
                {'id': 'state','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'sector_name','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'prime_movers','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'fuel_types','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'primary_technology','data_type':'text','cssClass':null,'headerCssClass':null},
                {'id': 'water_source','cssClass':null,'headerCssClass':null},
                {'id': 'water_type','cssClass':null,'headerCssClass':null},
                {'id': 'water_source_name','cssClass':null,'headerCssClass':null},
                {'id': 'water_discharge_name','cssClass':null,'headerCssClass':null}
            ];
            // Merge the configurations and return final column config
            var getColumnConfig = function(columnKey) {
                var columnConfig = defaultColumn;
                columnConfig.id = columnKey;
                var overrideColumn = overrides.find(function( obj ) {
                    return obj.id == columnKey;
                });
                if(overrideColumn != null) {
                    columnConfig = $.extend({}, defaultColumn, overrideColumn);
                }
                $.each(columnConfig, function (key, value) {
                    if (value === null) {
                        columnConfig[key] = '';
                    }
                });
                return columnConfig;
            };
            if(showPlantCompare) {
                columns.push({
                    'id': 'select_plant',
                    'width': 30,
                    'output': false,
                    'cssClass': 'grid-checkbox-column',
                    'visible':true,
                    'resizable': false,
                    'headerCssClass': 'grid-checkbox-column',
                    'maxSelected': maxSelected,
                    'displayFormatter': this.$class.checkboxItemFormatter
                });
            }
            var isTwoLineHeader = false;
            $.each(dataColumns, function(columnKey) {
                isTwoLineHeader |= (dataColumns[columnKey].indexOf('(') > -1);
            });

            var columnIndex = 0;
            var groupedColumns = this.hasGroupedColumns();
            $.each(dataColumns, function(columnKey) {
                var columnName = dataColumns[columnKey];
                var columnConfig = getColumnConfig(columnKey);
                if(isTwoLineHeader) {
                    columnName = columnName.replace(' (', '<BR>(');
                    columnConfig.headerCssClass += ' two-line-header';
                }

                var sortable;
                var sorter;
                var displayFormatter;
                var dataFormatter;
                var firstColumn = columnIndex === 0;
                sortable = firstColumn || !groupedColumns;

                if(firstColumn) {
                    displayFormatter = GridFormatters.hyperLinkCellFormatter;
                    dataFormatter = void 0;
                    if(sortable) {
                        columnConfig.headerCssClass += ' slick-header-column-sortable';
                        sorter = groupedColumns ? self.compareGroupedColumn : self.compareTextColumn;
                    }
                }
                else {
                    displayFormatter = void 0;
                    dataFormatter = columnConfig.id != 'plant_code' ? GridFormatters.numericalDataFormatter : void 0;
                    if(sortable) {
                        columnConfig.headerCssClass += ' slick-header-column-sortable';
                        switch(columnConfig.data_type) {
                            case 'text':
                                sorter = self.compareTextColumn;
                                break;
                            case 'number':
                                sorter = self.compareNumericColumn;
                                break;
                        }
                    }
                }
                columns.push({
                    'id': columnKey,
                    'name': columnName,
                    'field': columnKey,
                    'displayFormatter': displayFormatter,
                    'dataFormatter': dataFormatter,
                    'freq': freq,
                    'linkColumn': 'plant_code',
                    'sortable': sortable,
                    'sorter': sorter,
                    'headerCssClass': columnConfig.headerCssClass,
                    'cssClass': columnConfig.cssClass
                });
                columnIndex++;
            });
            return columns;
        },
        compareGroupedColumn: function (a, b, args) {
            var compare = function (a, b, sortOrder) {
                return ((sortOrder == null || sortOrder) ? (a > b ? 1 : -1) : (a > b ? -1 : 1));
            };
            var compareAsc = function (a, b) {
                return (a > b ? 1 : -1);
            };
            return a.group_by_column != b.group_by_column ? compare(a.group_by_column, b.group_by_column, args.sortAsc) : (
                a.cooling_system_id === undefined ? -1 : (
                    b.cooling_system_id === undefined ? 1 :
                        compareAsc(a.cooling_system_id,b.cooling_system_id)
                    )
                );
        },
        compareTextColumn: function (a, b, args) {
            a = a[args.sortCol.id].toLowerCase() ;
            b = b[args.sortCol.id].toLowerCase() ;
            return ((args.sortAsc == null || args.sortAsc) ? (a > b ? 1 : -1) : (a > b ? -1 : 1));
        },
        compareNumericColumn: function (a, b, args) {
            a = parseFloat(a[args.sortCol.id]);
            b = parseFloat(b[args.sortCol.id]);
            a  = isNaN(a) ? 0.1 : a;
            b  = isNaN(b) ? 0.1 : b;
            return ((args.sortAsc == null || args.sortAsc) ? (a > b ? 1 : -1) : (a > b ? -1 : 1));
        },
        configureSort: function() {
            this.getViewComponent().slickgrid.onSort.unsubscribe(this.onCustomSort);
            this.getViewComponent().slickgrid.onSort.subscribe(this.onCustomSort);
        },
        onCustomSort: function (e, args) {
            // Delegate the sorting to DataView, fire the change events and update the grid.
            if(this.getData().getLength() > 0) {
                this.getData().sort(function (dataRow1, dataRow2) {
                    return args.sortCol.sorter(dataRow1,dataRow2, args); // sorter property from column definition
                });

            }
        },
        hasGroupedColumns: function() {
            return this.getData().getLength() > 0 && this.getData().getItem(0).group_by_column != null;
        },
        getData: function() {
            return this.facade.retrieveProxy(TableDataProxy.NAME).getTableData();
        },
        addSubTitle : function() {
            var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
                parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                subTitle = tableDataProxy.getTableSubTitle(parameterProxy.getTab());
            this.removeSubTitle();
            if(subTitle != null) {
                $(this.getViewComponent().container).before('<div id="subTitleContainer">'+subTitle+'</div>');
            }
        },
        removeSubTitle : function() {
            $('#subTitleContainer').remove();
        },
        onRemove : function() {
            this.getViewComponent().destroy();
        },

        __classvars__: {

            NAME: 'DataGridMediator',

            checkboxItemFormatter: function(row, cell, value, columnDef, datum, dataContext) {
                return Mustache.render(CheckboxItemTemplate, {
                    'id':datum.plant_code,
                    'cssClass':columnDef.cssClass,
                    'checked':datum.checked,
                    'disabled':columnDef.maxSelected && !datum.checked,
                    'visible':columnDef.visible && datum.plant_code !== undefined
                });
            }
        }
    });
});

define('js/view/MediatorTab',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy'
], function(
	puremvc,
	Facade,
	ConfigProxy,
	ParameterProxy,
  TableDataProxy
) {
	return puremvc.Mediator.$extend({

		__init__: function(viewComponent) {
			this.$super(this.$class.NAME, viewComponent);
		},

		onRegister: function() {
			var self = this;
			this.getViewComponent().tabClicked.add(function(selectedTab) {
				self.sendNotification(Facade.TAB_SELECTION_UPDATED, {'tab' : selectedTab});
			});
		},

		listNotificationInterests: function() {
			return this.$super().concat([
					Facade.TAB_PARAMS_UPDATED
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);

			switch (note.getName()) {
        case Facade.TAB_PARAMS_UPDATED:
					vc.updateTabSelection(parameterProxy.getTab());
					break;
				default:
					break;
			}
		},

		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {
			NAME: 'TabMediator'
		}
	});
});

define('js/view/MediatorTimePeriodSelectionButton',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
    'js/model/TableDataProxy'
], function(
		puremvc,
		Facade,
		ConfigProxy,
		ParameterProxy,
        TableDataProxy
) {
	return puremvc.Mediator.$extend({
		__init__: function(viewComponent) {
			this.$super(this.$class.NAME, viewComponent);
		},

		listNotificationInterests: function() {
			return this.$super().concat([
					Facade.TIME_PARAMS_UPDATED,
					Facade.TOPIC_PARAMS_UPDATED,
                    Facade.TABLEDATA_UPDATED
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
                tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);

			switch (note.getName()) {
				case Facade.TIME_PARAMS_UPDATED:
					vc.updateTimeSelections(parameterProxy.getDatecode(),parameterProxy.getFreq());
					break;
				case Facade.TOPIC_PARAMS_UPDATED:
					vc.updateConfig(tableDataProxy.getTableDataStartEndDates(), parameterProxy.getFreq());
					break;
                case Facade.TABLEDATA_UPDATED:
                    vc.updateConfig(tableDataProxy.getTableDataStartEndDates(), parameterProxy.getFreq());
                    break;
				default:
					this.$super(note);
					break;
			}
		},


		onRegister: function() {
			var self = this;
			
			this.getViewComponent().timePeriodSelected.add(function(params) {
				var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
				var datecode = "";
				switch(parameterProxy.getFreq()){
					case "A":
						datecode += parseInt(params.year);
						break;
					case "Q":
						datecode += (parseInt(params.year) * 100 + parseInt(params.quarter));
						break;
					case "M":
						datecode += (parseInt(params.year) * 100 + parseInt(params.month));
						break;
					default:
				}
				self.getViewComponent().hideMenu();
				self.sendNotification(Facade.TIME_SELECTION_UPDATED, {datecode : datecode});
			});

		},


		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {
			NAME: 'TimePeriodMediator'
		}
	});
});

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/MediatorPlantSelection',[
    '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);
        },
        listNotificationInterests: function() {
            return this.$super().concat([
                Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED,
                Facade.PLANT_COMPARE_SELECTION_UPDATED
            ]);
        },
        onRegister: function() {
            var self = this;

            // Plant compare click
            this.viewComponent.toggleCompareClicked.add(function() {
                self.sendNotification(Facade.PLANT_COMPARE_TOGGLE_CLICKED);
            });

            // Plant compare close click
            this.viewComponent.closeButtonClicked.add(function() {
                self.sendNotification(Facade.PLANT_COMPARE_TOGGLE_CLICKED);
            });

            // Plant compare submit click
            this.viewComponent.submitButtonClicked.add(function() {
                // Validate at least one plant is selected
                var parameterProxy = self.facade.retrieveProxy(ParameterProxy.NAME);
                if(parameterProxy.getCountSelectPlants() >= 1) {
                    var body = {
                        codes: parameterProxy.getPlantCodeString(),
                        freq: parameterProxy.getTimeParams().freq,
                        datecode: parameterProxy.getTimeParams().datecode
                    };
                    self.sendNotification(Facade.PLANT_COMPARE_SUBMIT_CLICKED, body);
                }
            });
        },
        handleNotification: function(note) {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            switch (note.getName()) {
                case Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED:
                    if(parameterProxy.getShowPlantCompare()) {
                        this.viewComponent.showForm();
                    }
                    else {
                        this.viewComponent.hideForm();
                    }
                    break;
                case Facade.PLANT_COMPARE_SELECTION_UPDATED:
                    this.viewComponent.loadForm(parameterProxy.getPlants());
                    break;
                default:
                    this.$super(note);
            }
        },
        __classvars__: {
            NAME: 'PlantSelectionMediator'
        }
    });
});

define('js/view/VC_Tabs',[
    'classy',
    'signals',
    'text!templates/tabs.html',
    'text!templates/frequency_selection_button.html',
    'ie-array-find-polyfill'
], function(
    Class,
    signals,
    tabsTemplate,
    frequencySelectionButtonTemplate
    ){
    var VC_Tabs = Class.$extend({

        form: null,
        button: null,
        tabOptions: null,

        formSubmitted: null,
        formCanceled: null,
        formButtonClicked: null,
        formReset: null,
        resizeCallback: null,

        opened: false,

        __init__: function(container, config) {
            var self = this;

            this.container = container;
            this.config = $.extend({}, (config ? config : this.$class.defaultConfig));
            this.tabClicked = new signals.Signal();
            this.tabOptions = this.config.tabOptions;

            this.generateTabs(this.tabOptions);

            $('#'+this.tabOptions.tabs.tabId, self.container).buttonset();
            if(this.tabOptions.subTabs !== undefined) {
                $.each(this.tabOptions.subTabs, function (key, tab) {
                    $('#'+tab.tabId, self.container).buttonset();
                });
            }
            var childTabValue, selectedTab;
            $(this.container).on('change', 'input', function tabChange(e) {

                var tabGroup = self.tabOptions.tabs.tabGroup;
                selectedTab = $('input[name='+tabGroup+']:checked', self.container).val();
                if(selectedTab === undefined) {
                    selectedTab = $('input[name='+tabGroup+']:first', self.container).val();
                }
                childTabValue = $('input[name='+selectedTab+']:checked', self.container).val();
                if(childTabValue === undefined) {
                    childTabValue = $('input[name='+selectedTab+']:first', self.container).val();
                }
                if(childTabValue !== undefined) {
                    selectedTab += '-' + childTabValue;
                }
                self.tabClicked.dispatch(selectedTab);
            });
        },
        generateTabs : function(options) {
            this.container.html(Mustache.render(tabsTemplate, options, {
                'frequency_selection_button': frequencySelectionButtonTemplate
            }));
        },
        updateTabSelection : function(selectedTab) {
            var self = this;
            var selSubTabConfig;
            var selSubTab, i, j, tabConfig, tab;
						var parentTab = selectedTab;

            if(this.tabOptions.subTabs !== undefined) {
               /* outerFor:
                    for(i = 0; i < this.tabOptions.subTabs.length; i++) {
                        tabConfig = this.tabOptions.subTabs[i];
                        for(j = 0; j < tabConfig.tabList.length; j++) {
                            tab = tabConfig.tabList[j];
                            if(tab.id == selectedTab) {
                                selSubTabConfig = tab;
                                break outerFor;
                            }
                        }
                    }*/
                
                selSubTabConfig = this.tabOptions.subTabs.find(function(tabConfig) {
                    selSubTab = tabConfig.tabList.find(function(tab) {
                        return tab.id == selectedTab;
                    });
                    return selSubTab;
                });
                if(selSubTabConfig) {
                    parentTab = selSubTabConfig.tabGroup;
                }
            }
            // Hide sub tabs
            if(this.tabOptions.subTabs !== undefined) {
                $.each(this.tabOptions.subTabs, function (key, tab) {
                    $('#'+tab.selector, self.container).hide();
                });
            }
            var tabGroup = this.tabOptions.tabs.tabGroup;
            $('input[name='+tabGroup+'][value=' + parentTab + ']', self.container).prop('checked', true);
            if(selSubTabConfig !== undefined && selSubTab !== undefined) {
                $('input[name=' + selSubTab.name + '][value=' + selSubTab.value + ']', self.container).prop('checked', true);
                $('#'+selSubTabConfig.selector).show();
            }
            $('#'+this.tabOptions.tabs.tabId, self.container).buttonset("refresh");
            if(this.tabOptions.subTabs !== undefined) {
                $.each(this.tabOptions.subTabs, function (key, tab) {
                    $('#'+tab.tabId, self.container).buttonset("refresh");
                });
            }
        },
        destroy: function() {
            $(this.container).off('change', 'input[name="tabs"]', function(e){
                self.tabClicked.dispatch(e.target.value);
            });
        },


        __classvars__: {
            defaultConfig: {
                'tabOptions': {
                    'tabs':{
                        'selector':'selection-container',
                        'tabId':'parent-tabs',
                        'tabGroup':'tabs',
                        'tabList':[
                            {'id':'overview','value':"overview",'name':"tabs",'label':"Overview"},
                            {'id':'generation','value':"generation",'name':"tabs",'label':"Generation"},
                            {'id':'consumption-parent','value':"consumption",'name':"tabs",'label':"Consumption"},
                            {'id':'water-parent','value':"water",'name':"tabs",'label':"Water"},
                            {'id':'annual_emissions','value':"annual_emissions",'name':"tabs",'label':"Annual emissions"}
                        ]},
                    'subTabs':[{
                        'selector':'consumption_selectors',
                        'tabId':'consumption-tabs',
                        'tabGroup':'consumption',
                        'parentTabId':'consumption-parent',
                        'seperator':'Consumption by ...',
                        'tabList':[
                            {'id':'consumption-quantity','value':"quantity",'name':"consumption",'label':'quantity'},
                            {'id':'consumption-mmbtu_unit','value':"mmbtu_unit",'name':"consumption",'label':'mmBtu per unit'},
                            {'id':'consumption-mmbtu','value':"mmbtu",'name':"consumption",'label':'mmBtu'}
                        ]}, {
                        'selector':'water_selectors',
                        'tabId':'water-tabs',
                        'tabGroup':'water',
                        'parentTabId':'water-parent',
                        'seperator':'Water by ...',
                        'tabList':[
                            {'id':'water-rate','value':"rate",'name':"water",'label':'Rate'},
                            {'id':'water-volume','value':"volume",'name':"water",'label':'Volume'},
                            {'id':'water-intensity','value':"intensity",'name':"water",'label':'Intensity & fuel consumption'},
                            {'id':'water-source','value':"source",'name':"water",'label':'Source & discharge name'}
                        ]}
                    ]
                }
            }
        }
    });

    return VC_Tabs;
});


define('text!templates/time_period_selection_box.html',[],function () { return '<div class="time_selection_container" id="time_period_selection_box">\r\n  <h2>Change date range</h2>\r\n  <hr>\r\n  <div id="year-selector">\r\n    <div class="label">\r\n      Year\r\n    </div>\r\n\t\t<div id="timeSliderContainer">\r\n\t\t\t<div class="labelContainer single"></div>\r\n\t\t\t<div class="timeSlider"></div>\r\n\t\t</div>\r\n  </div>\r\n  <div id="month-selector">\r\n    <div class="label">\r\n      Month\r\n    </div>\r\n    <div id="month-buttons">\r\n  \t\t<label for="january">JAN</label>\r\n  \t\t<input name="month" id="january" type="radio" value="1">\r\n  \t\t<label for="february">FEB</label>\r\n  \t\t<input name="month" id="february" type="radio" value="2">\r\n  \t\t<label for="march">MAR</label>\r\n  \t\t<input name="month" id="march" type="radio" value="3">\r\n      <label for="april">APR</label>\r\n  \t\t<input name="month" id="april" type="radio" value="4">\r\n\t\t\t<br>\r\n  \t\t<label for="may">MAY</label>\r\n  \t\t<input name="month" id="may" type="radio" value="5">\r\n  \t\t<label for="june">JUN</label>\r\n  \t\t<input name="month" id="june" type="radio" value="6">\r\n      <label for="july">JUL</label>\r\n  \t\t<input name="month" id="july" type="radio" value="7">\r\n  \t\t<label for="august">AUG</label>\r\n  \t\t<input name="month" id="august" type="radio" value="8">\r\n\t\t\t<br>\r\n  \t\t<label for="september">SEP</label>\r\n  \t\t<input name="month" id="september" type="radio" value="9">\r\n      <label for="october">OCT</label>\r\n  \t\t<input name="month" id="october" type="radio" value="10">\r\n  \t\t<label for="november">NOV</label>\r\n  \t\t<input name="month" id="november" type="radio" value="11">\r\n  \t\t<label for="december">DEC</label>\r\n  \t\t<input name="month" id="december" type="radio" value="12">\r\n  \t</div>\r\n  </div>\r\n  <div id="quarter-selector">\r\n    <div class="label">\r\n      Quarter\r\n    </div>\r\n    <div id="quarter-buttons">\r\n  \t\t<label for="first-quarter">1st</label>\r\n  \t\t<input name="quarter" id="first-quarter" type="radio" value="1">\r\n  \t\t<label for="second-quarter">2nd</label>\r\n  \t\t<input name="quarter" id="second-quarter" type="radio" value="2">\r\n  \t\t<label for="third-quarter">3rd</label>\r\n  \t\t<input name="quarter" id="third-quarter" type="radio" value="3">\r\n      <label for="fourth-quarter">4th</label>\r\n      <input name="quarter" id="fourth-quarter" type="radio" value="4">\r\n  \t</div>\r\n  </div>\r\n  <hr>\r\n  <div id="time-period-button-container">\r\n    <span id="time-period-submit">\r\n      <span>Submit</span>\r\n    </span>\r\n    <span id="time-period-cancel">\r\n      <span>Cancel</span>\r\n    </span>\r\n  </div>\r\n</div>\r\n';});

define('js/view/VC_Time_Period_Selection_Button',[
  'classy',
  'text!templates/time_period_selection_box.html',
  'signals',
  'mustache',
  'simpleModal',
	'timeSlider'
],function(Class, timePeriodBoxTemplate, signals) {
  return Class.$extend({

    __init__: function(container, config) {
      var self = this;

      this.container = container;
      this.button = $('#time-period-selection-button-container');
      this.timePeriodSelected = new signals.Signal();

			this.container.empty().append($(Mustache.to_html(timePeriodBoxTemplate)));

			this.timeSlider = $('.timeSlider', this.container);
			this.timeSlider.slider({
				'orientation': 'horizontal',
				'range': false,
				'slide' : function(event, ui) {
					self.updateSliderLabel(ui.value);
				},
				'change' : function(event, ui) {
					self.updateSliderLabel(ui.value);
					self.toggleQuarterMonths(ui.value);
				}
			});
			
			this.quarterButtonset = $('#quarter-buttons', this.container);
			this.monthButtonset = $('#month-buttons', this.container);

			this.quarterButtonset.buttonset();
			this.monthButtonset.buttonset();

			this.updateConfig(config, 'A');

			this.buttonClickHandler = function(e) {
				if (!(self.button.hasClass('disabled') || self.button.hasClass('open'))) {
					self.showMenu();
				}
			};

      this.button.on('click', this.buttonClickHandler);

			this.timePeriodSubmitHandler = function(){
				var params = {};
				params.year = self.timeSlider.slider('value');
				params.month = $('input:checked', self.monthButtonset).val();
				params.quarter = $('input:checked', self.quarterButtonset).val();
				self.hideMenu();
				self.timePeriodSelected.dispatch(params);
			};

      $(this.container).on('click', '#time-period-submit', this.timePeriodSubmitHandler);

			this.timePeriodCloseHandler = function(){
				self.hideMenu();
			};
      $(this.container).on('click', '#time-period-cancel', this.timePeriodCloseHandler);

      this.modalCloseHandler = function(e) {
        if ($(e.target).parents('#time_dialog_container').length === 0) {
          self.hideMenu();
        }
      };
			
			this.resizeCallback = function() {
                var buttonOffset = {
                    'top' : self.button.position().top + self.button.parent().position().top,
                    'left' : self.button.position().left + self.button.parent().position().left
                };
				$('#simplemodal-container').css({
					'top': buttonOffset.top,
					'left': buttonOffset.left - $('#time_dialog_container').width()
				});
			};
    },
		
		updateConfig : function(config, freq) {
			this.config = config;
			
			this.updateYearSliderStartEnd(freq);
		},

		updateYearSliderStartEnd : function(freq) {
			var min, max;

			switch(freq) {
				case 'A':
					min = parseInt(this.config.annualStart);
					max = parseInt(this.config.annualEnd);
					break;
				case 'M':
					min = parseInt(this.config.monthlyStart / 100);
					max = parseInt(this.config.monthlyEnd / 100);
					break;
				case 'Q':
					min = parseInt(this.config.quarterlyStart / 100);
					max = parseInt(this.config.quarterlyEnd / 100);
					break;
			}

			this.timeSlider.slider("option", "min", min);
			this.timeSlider.slider("option", "max", max);


			var slider =  $('.timeSlider'),
				numTicks = max - min,
				spacing =  100 / (max - min);

				slider.find('.ui-slider-tick-mark').remove();
				for (var i = 0; i <= numTicks ; i++) {
					$('<span class="ui-slider-tick-mark"></span>').css('left', (spacing * i) +  '%').appendTo(slider);
				}


		},

		toggleQuarterMonths : function(year) {
			var config = this.config;

			$.each($('input', this.quarterButtonset), function(index, input) {
				var datecode = (year * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode > config.quarterlyEnd);
			});
			if($('input:checked', this.quarterButtonset).is(':disabled')) {
				$('input:enabled:last', this.quarterButtonset).prop('checked', true);
			}
			this.quarterButtonset.buttonset('refresh');

			$.each($('input', this.monthButtonset), function(index, input) {
				var datecode = (year * 100) + parseInt($(input).val());
				$(input).prop('disabled', datecode > config.monthlyEnd);
			});
			if($('input:checked', this.monthButtonset).is(':disabled')) {
				$('input:enabled:last', this.monthButtonset).prop('checked', true);
			}
			this.monthButtonset.buttonset('refresh');
		},
		
    updateButtonLabel : function(datecode,freq) {
      var date = datecode.toString();
      var textLabel = "";
      function dateToYear(y){
        return y.substring(0,4);
      }
      function dateToQuarter(q){
        var quarter = parseInt(q.substring(4,6))-1;
				var quarterNames = ["Q1", "Q2","Q3","Q4"];

				return quarterNames[quarter];
      }
      function dateToMonth(m){
        var month = parseInt(m.substring(4,6))-1;
				var monthNames = ["Jan","Feb","Mar","Apr","May","Jun", "Jul", "Aug","Sep","Oct","Nov","Dec"];

				return monthNames[month];
      }
      switch(freq) {
        case 'A':
          textLabel += date;
          break;
        case 'M':
          textLabel += dateToMonth(date) + " " + dateToYear(date);
          break;
        case 'Q':
          textLabel += dateToQuarter(date) + " " + dateToYear(date);
          break;
      }
      $('.text', this.button).text(textLabel);
    },

		updateSliderLabel : function(datecode) {
			var labelContainer = $('#timeSliderContainer .labelContainer', this.container);
			var sliderHandle = $('.timeSlider .ui-slider-handle', this.container);

			labelContainer.text(datecode.toString().substring(0,4));

			setTimeout(function() {
				var leftPosition = sliderHandle.position().left - (labelContainer.width()/2);

				labelContainer.css('left', leftPosition + 'px');
			}, 0);
		},

		updateTimeSelections : function(datecode, freq) {
			this.datecode = datecode;
			this.freq = freq;

			var date = datecode.toString();
			this.updateButtonLabel(datecode, freq);
			this.updateYearSliderStartEnd(freq);

				var year = parseInt(date.substring(0,4)), month, quarter;
			
				// set frequency class on container to show/hide month quarter selections
				switch(freq) {
					case 'A':
						$('#time_period_selection_box')
								.toggleClass('monthly quarterly', false)
								.toggleClass('annual', true);
						break;
					case 'M':
						$('#time_period_selection_box')
								.toggleClass('annual quarterly', false)
								.toggleClass('monthly', true);
						month = parseInt(date.substring(4, 6));
						quarter = Math.ceil(month/3);
						break;
					case 'Q':
						$('#time_period_selection_box')
								.toggleClass('monthly annual', false)
								.toggleClass('quarterly', true);
						quarter = parseInt(date.substring(4, 6));
						month = quarter * 3 - 2;
						break;
				}

			$('input[value=' + quarter + ']', this.quarterButtonset).prop('checked', true);
			$('input[value=' + month + ']', this.monthButtonset).prop('checked', true);
			this.timeSlider.slider("value", year);
			this.updateSliderLabel(datecode);
		},

    showMenu: function() {
      var self = this;
			this.button.toggleClass("open", true);
        var buttonOffset = {
            'top' : self.button.position().top + self.button.offsetParent().position().top,
            'left' : self.button.position().left + self.button.offsetParent().position().left
        };

      $('#time_dialog_container').css({
        'top': buttonOffset.top + self.button.height(),
				'left': buttonOffset.left + self.button.width() - $('#time_dialog_container').width(),
		  'z-index':'1200'
      }).show();

      // 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.
			setTimeout(function() {
				$('body').on('click', self.modalCloseHandler);
			}, 0);

      $(window).resize(this.resizeCallback = function() {
          var buttonOffset = {
              'top' : self.button.position().top + self.button.offsetParent().position().top,
              'left' : self.button.position().left + self.button.offsetParent().position().left
          };
        $('#simplemodal-container').css({
          'top': buttonOffset.top + self.button.height()-1,
          'left': buttonOffset.left + self.button.width() - $('#time_dialog_container').width()
        });
      });

			
			$(window).on('resize', this.resizeCallback);

			this.updateSliderLabel(this.datecode);
    },

    hideMenu: function() {
			this.button.toggleClass("open", false);


			$('#time_dialog_container').hide();
			//reset selections
			this.updateTimeSelections(this.datecode, this.freq);

      if (typeof this.resizeCallback == 'function') {
        $(window).off('resize', this.resizeCallback);
      }
      if (typeof this.modalCloseHandler == 'function') {
        $('body').off('click', this.modalCloseHandler);
      }

      this.resizeCallback = null;
    },

		destroy : function() {
			this.button.off('click', this.buttonClickHandler);
			$(this.container).off('click', '#time-period-submit', this.timePeriodSubmitHandler);
			$(this.container).off('click', '#time-period-cancel', this.timePeriodCloseHandler);

			this.quarterButtonset.buttonset('destroy');
			this.monthButtonset.buttonset('destroy');
			this.timeSlider.slider('destroy');

			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/searchBox.html',[],function () { return '<div id="searchbox">\r\n\t<input type="text" name="searchInput" placeholder="{{searchPrompt}}">\r\n</div>\r\n<div class="search_controls">\r\n\t<a id="search_box_clear" class="clear"><span>Clear</span></a>\r\n\t<a id="search_box_close" class="close"><span>Close</span></a>\r\n</div>\r\n';});

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
					)
				);

				$("#search_box_close").click(function(){
					$("#search_time_container").toggle();
					$("#search_plants_button .line").toggle();
				});
				$("#search_plants_button").click(function(){
					$("#search_plants_button .line").toggle();
					$("#search_time_container").toggle();
				});
				
				this.inputChanged = new signals.Signal();
				this.input = this.container.find('input');
				this.resetButton = this.container.find('#search_box_clear');
				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());
				});

				$("#search_time_container").hide();
				$("#search_plants_button .line").hide();
			},

			__classvars__: {
				templateOpts: {
					searchPrompt: 'Start typing to search all available fields'
				}
			}

		});
	});


define('text!templates/plant_selection_box.html',[],function () { return '\r\n<div id="plant_select_dialog">\r\n    <a id="plant_select_dialog_close_button"></a>\r\n    <div class="plant-select-item-list">\r\n\r\n        {{#plants}}\r\n        <div class="plant-select-item">\r\n\t\t\t\t\t\t<div id="map-{{code}}" class="map"></div>\r\n            <span class="plant-select-area">{{name}} {{#code}}({{code}}){{/code}}</span>\r\n        </div>\r\n        {{/plants}}\r\n        {{^plants}}\r\n        <div class="plant-select-item">\r\n           <span class="plant-select-area"> </span>\r\n        </div>\r\n        {{/plants}}\r\n    </div>\r\n    <div class="plant-select-submit {{#styles}}{{submitDisabled}}{{/styles}}">\r\n        <a id="plant_select_dialog_submit_button" class="{{#styles}}{{submitDisabled}}{{/styles}}">Compare selected plants</a>\r\n    </div>​\r\n</div>';});

/*
 Leaflet 1.0.3, a JS library for interactive maps. http://leafletjs.com
 (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
*/
!function(t,e,i){function n(){var e=t.L;o.noConflict=function(){return t.L=e,this},t.L=o}var o={version:"1.0.3"};"object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define('leaflet',o),"undefined"!=typeof t&&n(),o.Util={extend:function(t){var e,i,n,o;for(i=1,n=arguments.length;i<n;i++){o=arguments[i];for(e in o)t[e]=o[e]}return t},create:Object.create||function(){function t(){}return function(e){return t.prototype=e,new t}}(),bind:function(t,e){var i=Array.prototype.slice;if(t.bind)return t.bind.apply(t,i.call(arguments,1));var n=i.call(arguments,2);return function(){return t.apply(e,n.length?n.concat(i.call(arguments)):arguments)}},stamp:function(t){return t._leaflet_id=t._leaflet_id||++o.Util.lastId,t._leaflet_id},lastId:0,throttle:function(t,e,i){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(i,o),o=!1)},s=function(){n?o=arguments:(t.apply(i,arguments),setTimeout(r,e),n=!0)}},wrapNum:function(t,e,i){var n=e[1],o=e[0],s=n-o;return t===n&&i?t:((t-o)%s+s)%s+o},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){t.hasOwnProperty("options")||(t.options=t.options?o.Util.create(t.options):{});for(var i in e)t.options[i]=e[i];return t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&e.indexOf("?")!==-1?"&":"?")+n.join("&")},template:function(t,e){return t.replace(o.Util.templateRe,function(t,n){var o=e[n];if(o===i)throw new Error("No value provided for variable "+t);return"function"==typeof o&&(o=o(e)),o})},templateRe:/\{ *([\w_\-]+) *\}/g,isArray:Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},indexOf:function(t,e){for(var i=0;i<t.length;i++)if(t[i]===e)return i;return-1},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){return t["webkit"+e]||t["moz"+e]||t["ms"+e]}function i(e){var i=+new Date,o=Math.max(0,16-(i-n));return n=i+o,t.setTimeout(e,o)}var n=0,s=t.requestAnimationFrame||e("RequestAnimationFrame")||i,r=t.cancelAnimationFrame||e("CancelAnimationFrame")||e("CancelRequestAnimationFrame")||function(e){t.clearTimeout(e)};o.Util.requestAnimFrame=function(e,n,r){return r&&s===i?void e.call(n):s.call(t,o.bind(e,n))},o.Util.cancelAnimFrame=function(e){e&&r.call(t,e)}}(),o.extend=o.Util.extend,o.bind=o.Util.bind,o.stamp=o.Util.stamp,o.setOptions=o.Util.setOptions,o.Class=function(){},o.Class.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},i=e.__super__=this.prototype,n=o.Util.create(i);n.constructor=e,e.prototype=n;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&(e[s]=this[s]);return t.statics&&(o.extend(e,t.statics),delete t.statics),t.includes&&(o.Util.extend.apply(null,[n].concat(t.includes)),delete t.includes),n.options&&(t.options=o.Util.extend(o.Util.create(n.options),t.options)),o.extend(n,t),n._initHooks=[],n.callInitHooks=function(){if(!this._initHooksCalled){i.callInitHooks&&i.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=n._initHooks.length;t<e;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){return o.extend(this.prototype,t),this},o.Class.mergeOptions=function(t){return o.extend(this.prototype.options,t),this},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i),this},o.Evented=o.Class.extend({on:function(t,e,i){if("object"==typeof t)for(var n in t)this._on(n,t[n],e);else{t=o.Util.splitWords(t);for(var s=0,r=t.length;s<r;s++)this._on(t[s],e,i)}return this},off:function(t,e,i){if(t)if("object"==typeof t)for(var n in t)this._off(n,t[n],e);else{t=o.Util.splitWords(t);for(var s=0,r=t.length;s<r;s++)this._off(t[s],e,i)}else delete this._events;return this},_on:function(t,e,n){this._events=this._events||{};var o=this._events[t];o||(o=[],this._events[t]=o),n===this&&(n=i);for(var s={fn:e,ctx:n},r=o,a=0,h=r.length;a<h;a++)if(r[a].fn===e&&r[a].ctx===n)return;r.push(s)},_off:function(t,e,n){var s,r,a;if(this._events&&(s=this._events[t])){if(!e){for(r=0,a=s.length;r<a;r++)s[r].fn=o.Util.falseFn;return void delete this._events[t]}if(n===this&&(n=i),s)for(r=0,a=s.length;r<a;r++){var h=s[r];if(h.ctx===n&&h.fn===e)return h.fn=o.Util.falseFn,this._firingCount&&(this._events[t]=s=s.slice()),void s.splice(r,1)}}},fire:function(t,e,i){if(!this.listens(t,i))return this;var n=o.Util.extend({},e,{type:t,target:this});if(this._events){var s=this._events[t];if(s){this._firingCount=this._firingCount+1||1;for(var r=0,a=s.length;r<a;r++){var h=s[r];h.fn.call(h.ctx||this,n)}this._firingCount--}}return i&&this._propagateEvent(n),this},listens:function(t,e){var i=this._events&&this._events[t];if(i&&i.length)return!0;if(e)for(var n in this._eventParents)if(this._eventParents[n].listens(t,e))return!0;return!1},once:function(t,e,i){if("object"==typeof t){for(var n in t)this.once(n,t[n],e);return this}var s=o.bind(function(){this.off(t,e,i).off(t,s,i)},this);return this.on(t,e,i).on(t,s,i)},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[o.stamp(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[o.stamp(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,o.extend({layer:t.target},t),!0)}});var s=o.Evented.prototype;s.addEventListener=s.on,s.removeEventListener=s.clearAllEventListeners=s.off,s.addOneTimeEventListener=s.once,s.fireEvent=s.fire,s.hasEventListeners=s.listens,o.Mixin={Events:s},function(){var i=navigator.userAgent.toLowerCase(),n=e.documentElement,s="ActiveXObject"in t,r=i.indexOf("webkit")!==-1,a=i.indexOf("phantom")!==-1,h=i.search("android [23]")!==-1,l=i.indexOf("chrome")!==-1,u=i.indexOf("gecko")!==-1&&!r&&!t.opera&&!s,c=0===navigator.platform.indexOf("Win"),d="undefined"!=typeof orientation||i.indexOf("mobile")!==-1,_=!t.PointerEvent&&t.MSPointerEvent,m=t.PointerEvent||_,p=s&&"transition"in n.style,f="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix&&!h,g="MozPerspective"in n.style,v="OTransition"in n.style,y=!t.L_NO_TOUCH&&(m||"ontouchstart"in t||t.DocumentTouch&&e instanceof t.DocumentTouch);o.Browser={ie:s,ielt9:s&&!e.addEventListener,edge:"msLaunchUri"in navigator&&!("documentMode"in e),webkit:r,gecko:u,android:i.indexOf("android")!==-1,android23:h,chrome:l,safari:!l&&i.indexOf("safari")!==-1,win:c,ie3d:p,webkit3d:f,gecko3d:g,opera12:v,any3d:!t.L_DISABLE_3D&&(p||f||g)&&!v&&!a,mobile:d,mobileWebkit:d&&r,mobileWebkit3d:d&&f,mobileOpera:d&&t.opera,mobileGecko:d&&u,touch:!!y,msPointer:!!_,pointer:!!m,retina:(t.devicePixelRatio||t.screen.deviceXDPI/t.screen.logicalXDPI)>1}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new o.Point(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new o.Point(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new o.Point(t.x,t.y):new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;n<o;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,r=s.x>=e.x&&n.x<=i.x,a=s.y>=e.y&&n.y<=i.y;return r&&a},overlaps:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,r=s.x>e.x&&n.x<i.x,a=s.y>e.y&&n.y<i.y;return r&&a},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},create:function(t,i,n){var o=e.createElement(t);return o.className=i||"",n&&n.appendChild(o),o},remove:function(t){var e=t.parentNode;e&&e.removeChild(t)},empty:function(t){for(;t.firstChild;)t.removeChild(t.firstChild)},toFront:function(t){t.parentNode.appendChild(t)},toBack:function(t){var e=t.parentNode;e.insertBefore(t,e.firstChild)},hasClass:function(t,e){if(t.classList!==i)return t.classList.contains(e);var n=o.DomUtil.getClass(t);return n.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(n)},addClass:function(t,e){if(t.classList!==i)for(var n=o.Util.splitWords(e),s=0,r=n.length;s<r;s++)t.classList.add(n[s]);else if(!o.DomUtil.hasClass(t,e)){var a=o.DomUtil.getClass(t);o.DomUtil.setClass(t,(a?a+" ":"")+e)}},removeClass:function(t,e){t.classList!==i?t.classList.remove(e):o.DomUtil.setClass(t,o.Util.trim((" "+o.DomUtil.getClass(t)+" ").replace(" "+e+" "," ")))},setClass:function(t,e){t.className.baseVal===i?t.className=e:t.className.baseVal=e},getClass:function(t){return t.className.baseVal===i?t.className:t.className.baseVal},setOpacity:function(t,e){"opacity"in t.style?t.style.opacity=e:"filter"in t.style&&o.DomUtil._setOpacityIE(t,e)},_setOpacityIE:function(t,e){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(t){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"},testProp:function(t){for(var i=e.documentElement.style,n=0;n<t.length;n++)if(t[n]in i)return t[n];return!1},setTransform:function(t,e,i){var n=e||new o.Point(0,0);t.style[o.DomUtil.TRANSFORM]=(o.Browser.ie3d?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(i?" scale("+i+")":"")},setPosition:function(t,e){t._leaflet_pos=e,o.Browser.any3d?o.DomUtil.setTransform(t,e):(t.style.left=e.x+"px",t.style.top=e.y+"px")},getPosition:function(t){return t._leaflet_pos||new o.Point(0,0)}},function(){o.DomUtil.TRANSFORM=o.DomUtil.testProp(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]);var i=o.DomUtil.TRANSITION=o.DomUtil.testProp(["webkitTransition","transition","OTransition","MozTransition","msTransition"]);if(o.DomUtil.TRANSITION_END="webkitTransition"===i||"OTransition"===i?i+"End":"transitionend","onselectstart"in e)o.DomUtil.disableTextSelection=function(){o.DomEvent.on(t,"selectstart",o.DomEvent.preventDefault)},o.DomUtil.enableTextSelection=function(){o.DomEvent.off(t,"selectstart",o.DomEvent.preventDefault)};else{var n=o.DomUtil.testProp(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);o.DomUtil.disableTextSelection=function(){if(n){var t=e.documentElement.style;this._userSelect=t[n],t[n]="none"}},o.DomUtil.enableTextSelection=function(){n&&(e.documentElement.style[n]=this._userSelect,delete this._userSelect)}}o.DomUtil.disableImageDrag=function(){o.DomEvent.on(t,"dragstart",o.DomEvent.preventDefault)},o.DomUtil.enableImageDrag=function(){o.DomEvent.off(t,"dragstart",o.DomEvent.preventDefault)},o.DomUtil.preventOutline=function(e){for(;e.tabIndex===-1;)e=e.parentNode;e&&e.style&&(o.DomUtil.restoreOutline(),this._outlineElement=e,this._outlineStyle=e.style.outline,e.style.outline="none",o.DomEvent.on(t,"keydown",o.DomUtil.restoreOutline,this))},o.DomUtil.restoreOutline=function(){this._outlineElement&&(this._outlineElement.style.outline=this._outlineStyle,delete this._outlineElement,delete this._outlineStyle,o.DomEvent.off(t,"keydown",o.DomUtil.restoreOutline,this))}}(),o.LatLng=function(t,e,n){if(isNaN(t)||isNaN(e))throw new Error("Invalid LatLng object: ("+t+", "+e+")");this.lat=+t,this.lng=+e,n!==i&&(this.alt=+n)},o.LatLng.prototype={equals:function(t,e){if(!t)return!1;t=o.latLng(t);var n=Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng));return n<=(e===i?1e-9:e)},toString:function(t){return"LatLng("+o.Util.formatNum(this.lat,t)+", "+o.Util.formatNum(this.lng,t)+")"},distanceTo:function(t){return o.CRS.Earth.distance(this,o.latLng(t))},wrap:function(){return o.CRS.Earth.wrapLatLng(this)},toBounds:function(t){var e=180*t/40075017,i=e/Math.cos(Math.PI/180*this.lat);return o.latLngBounds([this.lat-e,this.lng-i],[this.lat+e,this.lng+i])},clone:function(){return new o.LatLng(this.lat,this.lng,this.alt)}},o.latLng=function(t,e,n){return t instanceof o.LatLng?t:o.Util.isArray(t)&&"object"!=typeof t[0]?3===t.length?new o.LatLng(t[0],t[1],t[2]):2===t.length?new o.LatLng(t[0],t[1]):null:t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon,t.alt):e===i?null:new o.LatLng(t,e,n)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;n<o;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){var e,i,n=this._southWest,s=this._northEast;if(t instanceof o.LatLng)e=t,i=t;else{if(!(t instanceof o.LatLngBounds))return t?this.extend(o.latLng(t)||o.latLngBounds(t)):this;if(e=t._southWest,i=t._northEast,!e||!i)return this}return n||s?(n.lat=Math.min(e.lat,n.lat),n.lng=Math.min(e.lng,n.lng),s.lat=Math.max(i.lat,s.lat),s.lng=Math.max(i.lng,s.lng)):(this._southWest=new o.LatLng(e.lat,e.lng),this._northEast=new o.LatLng(i.lat,i.lng)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof o.LatLng||"lat"in t?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),r=s.lat>=e.lat&&n.lat<=i.lat,a=s.lng>=e.lng&&n.lng<=i.lng;return r&&a},overlaps:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),r=s.lat>e.lat&&n.lat<i.lat,a=s.lng>e.lng&&n.lng<i.lng;return r&&a},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return!!t&&(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast()))},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)},bounds:o.bounds([-180,-90],[180,90])},o.Projection.SphericalMercator={R:6378137,MAX_LATITUDE:85.0511287798,project:function(t){var e=Math.PI/180,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=Math.sin(n*e);return new o.Point(this.R*t.lng*e,this.R*Math.log((1+s)/(1-s))/2)},unproject:function(t){var e=180/Math.PI;return new o.LatLng((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*e,t.x*e/this.R)},bounds:function(){var t=6378137*Math.PI;return o.bounds([-t,-t],[t,t])}()},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){if(this.infinite)return null;var e=this.projection.bounds,i=this.scale(t),n=this.transformation.transform(e.min,i),s=this.transformation.transform(e.max,i);return o.bounds(n,s)},infinite:!1,wrapLatLng:function(t){var e=this.wrapLng?o.Util.wrapNum(t.lng,this.wrapLng,!0):t.lng,i=this.wrapLat?o.Util.wrapNum(t.lat,this.wrapLat,!0):t.lat,n=t.alt;return o.latLng(i,e,n)},wrapLatLngBounds:function(t){var e=t.getCenter(),i=this.wrapLatLng(e),n=e.lat-i.lat,s=e.lng-i.lng;if(0===n&&0===s)return t;var r=t.getSouthWest(),a=t.getNorthEast(),h=o.latLng({lat:r.lat-n,lng:r.lng-s}),l=o.latLng({lat:a.lat-n,lng:a.lng-s});return new o.LatLngBounds(h,l)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,e){var i=e.lng-t.lng,n=e.lat-t.lat;return Math.sqrt(i*i+n*n)},infinite:!0}),o.CRS.Earth=o.extend({},o.CRS,{wrapLng:[-180,180],R:6371e3,distance:function(t,e){var i=Math.PI/180,n=t.lat*i,o=e.lat*i,s=Math.sin(n)*Math.sin(o)+Math.cos(n)*Math.cos(o)*Math.cos((e.lng-t.lng)*i);return this.R*Math.acos(Math.min(s,1))}}),o.CRS.EPSG3857=o.extend({},o.CRS.Earth,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:function(){var t=.5/(Math.PI*o.Projection.SphericalMercator.R);return new o.Transformation(t,.5,-t,.5)}()}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS.Earth,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/180,1,-1/180,.5)}),o.Map=o.Evented.extend({options:{crs:o.CRS.EPSG3857,center:i,zoom:i,minZoom:i,maxZoom:i,layers:[],maxBounds:i,renderer:i,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._onResize=o.bind(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.zoom!==i&&(this._zoom=this._limitZoom(e.zoom)),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this.callInitHooks(),this._zoomAnimated=o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.mobileOpera&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),o.DomEvent.on(this._proxy,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=this._limitCenter(o.latLng(t),e,this.options.maxBounds),n=n||{},this._stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate,duration:n.duration},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=t,this)},zoomIn:function(t,e){return t=t||(o.Browser.any3d?this.options.zoomDelta:1),this.setZoom(this._zoom+t,e)},zoomOut:function(t,e){return t=t||(o.Browser.any3d?this.options.zoomDelta:1),this.setZoom(this._zoom-t,e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),r=t instanceof o.Point?t:this.latLngToContainerPoint(t),a=r.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(a));return this.setView(h,e,{zoom:i})},_getBoundsCenterZoom:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n));s="number"==typeof e.maxZoom?Math.min(e.maxZoom,s):s;var r=n.subtract(i).divideBy(2),a=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(a.add(h).divideBy(2).add(r),s);return{center:l,zoom:s}},fitBounds:function(t,e){if(t=o.latLngBounds(t),!t.isValid())throw new Error("Bounds are not valid.");var i=this._getBoundsCenterZoom(t,e);return this.setView(i.center,i.zoom,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this.fire("moveend");if(e.animate!==!0&&!this.getSize().contains(t))return this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t).round();this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},flyTo:function(t,e,n){function s(t){var e=t?-1:1,i=t?v:g,n=v*v-g*g+e*L*L*y*y,o=2*i*L*y,s=n/o,r=Math.sqrt(s*s+1)-s,a=r<1e-9?-18:Math.log(r);return a}function r(t){return(Math.exp(t)-Math.exp(-t))/2}function a(t){return(Math.exp(t)+Math.exp(-t))/2}function h(t){return r(t)/a(t)}function l(t){return g*(a(x)/a(x+P*t))}function u(t){return g*(a(x)*h(x+P*t)-r(x))/L}function c(t){return 1-Math.pow(1-t,1.5)}function d(){var i=(Date.now()-w)/T,n=c(i)*b;i<=1?(this._flyToFrame=o.Util.requestAnimFrame(d,this),this._move(this.unproject(_.add(m.subtract(_).multiplyBy(u(n)/y)),f),this.getScaleZoom(g/l(n),f),{flyTo:!0})):this._move(t,e)._moveEnd(!0)}if(n=n||{},n.animate===!1||!o.Browser.any3d)return this.setView(t,e,n);this._stop();var _=this.project(this.getCenter()),m=this.project(t),p=this.getSize(),f=this._zoom;t=o.latLng(t),e=e===i?f:e;var g=Math.max(p.x,p.y),v=g*this.getZoomScale(f,e),y=m.distanceTo(_)||1,P=1.42,L=P*P,x=s(0),w=Date.now(),b=(s(1)-x)/P,T=n.duration?1e3*n.duration:1e3*b*.8;return this._moveStart(!0),d.call(this),this},flyToBounds:function(t,e){var i=this._getBoundsCenterZoom(t,e);return this.flyTo(i.center,i.zoom,e)},setMaxBounds:function(t){return t=o.latLngBounds(t),t.isValid()?(this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds)):(this.options.maxBounds=null,this.off("moveend",this._panInsideMaxBounds))},setMinZoom:function(t){return this.options.minZoom=t,this._loaded&&this.getZoom()<this.options.minZoom?this.setZoom(t):this},setMaxZoom:function(t){return this.options.maxZoom=t,this._loaded&&this.getZoom()>this.options.maxZoom?this.setZoom(t):this},panInsideBounds:function(t,e){this._enforcingBounds=!0;var i=this.getCenter(),n=this._limitCenter(i,this._zoom,o.latLngBounds(t));return i.equals(n)||this.panTo(n,e),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=o.extend({animate:!1,pan:!0},t===!0?{animate:!0}:t);var e=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var i=this.getSize(),n=e.divideBy(2).round(),s=i.divideBy(2).round(),r=n.subtract(s);return r.x||r.y?(t.animate&&t.pan?this.panBy(r):(t.pan&&this._rawPanBy(r),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o.bind(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=o.extend({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=n.toBounds(t.coords.accuracy),r=this._locateOptions;if(r.setView){var a=this.getBoundsZoom(s);this.setView(n,r.maxZoom?Math.min(a,r.maxZoom):a)}var h={latlng:n,bounds:s,timestamp:t.timestamp};for(var l in t.coords)"number"==typeof t.coords[l]&&(h[l]=t.coords[l]);this.fire("locationfound",h)},addHandler:function(t,e){if(!e)return this;var i=this[t]=new e(this);return this._handlers.push(i),this.options[t]&&i.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=i,this._containerId=i}o.DomUtil.remove(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");for(var t in this._layers)this._layers[t].remove();return this},createPane:function(t,e){var i="leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),n=o.DomUtil.create("div",i,e||this._mapPane);return t&&(this._panes[t]=n),n},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds(),e=this.unproject(t.getBottomLeft()),i=this.unproject(t.getTopRight());return new o.LatLngBounds(e,i)},getMinZoom:function(){return this.options.minZoom===i?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===i?this._layersMaxZoom===i?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=o.latLngBounds(t),i=o.point(i||[0,0]);var n=this.getZoom()||0,s=this.getMinZoom(),r=this.getMaxZoom(),a=t.getNorthWest(),h=t.getSouthEast(),l=this.getSize().subtract(i),u=o.bounds(this.project(h,n),this.project(a,n)).getSize(),c=o.Browser.any3d?this.options.zoomSnap:1,d=Math.min(l.x/u.x,l.y/u.y);return n=this.getScaleZoom(d,n),c&&(n=Math.round(n/(c/100))*(c/100),n=e?Math.ceil(n/c)*c:Math.floor(n/c)*c),Math.max(s,Math.min(r,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new o.Point(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,e){var i=this._getTopLeftPoint(t,e);return new o.Bounds(i,i.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(t===i?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,e){var n=this.options.crs;return e=e===i?this._zoom:e,n.scale(t)/n.scale(e)},getScaleZoom:function(t,e){var n=this.options.crs;e=e===i?this._zoom:e;var o=n.zoom(t*n.scale(e));return isNaN(o)?1/0:o},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(o.latLng(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(o.latLngBounds(t))},distance:function(t,e){return this.options.crs.distance(o.latLng(t),o.latLng(e))},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet_id)throw new Error("Map container is already initialized.");o.DomEvent.addListener(e,"scroll",this._onScroll,this),this._containerId=o.Util.stamp(e)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&o.Browser.any3d,o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(o.Browser.ielt9?" leaflet-oldie":"")+(o.Browser.safari?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));
var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,"leaflet-zoom-hide"),o.DomUtil.addClass(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,e){o.DomUtil.setPosition(this._mapPane,new o.Point(0,0));var i=!this._loaded;this._loaded=!0,e=this._limitZoom(e),this.fire("viewprereset");var n=this._zoom!==e;this._moveStart(n)._move(t,e)._moveEnd(n),this.fire("viewreset"),i&&this.fire("load")},_moveStart:function(t){return t&&this.fire("zoomstart"),this.fire("movestart")},_move:function(t,e,n){e===i&&(e=this._zoom);var o=this._zoom!==e;return this._zoom=e,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(o||n&&n.pinch)&&this.fire("zoom",n),this.fire("move",n)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return o.Util.cancelAnimFrame(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){this._targets={},this._targets[o.stamp(this._container)]=this;var i=e?"off":"on";o.DomEvent[i](this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&o.DomEvent[i](t,"resize",this._onResize,this),o.Browser.any3d&&this.options.transform3DLimit&&this[i]("moveend",this._onMoveEnd)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,e){for(var i,n=[],s="mouseout"===e||"mouseover"===e,r=t.target||t.srcElement,a=!1;r;){if(i=this._targets[o.stamp(r)],i&&("click"===e||"preclick"===e)&&!t._simulated&&this._draggableMoved(i)){a=!0;break}if(i&&i.listens(e,!0)){if(s&&!o.DomEvent._isExternalTarget(r,t))break;if(n.push(i),s)break}if(r===this._container)break;r=r.parentNode}return n.length||a||s||!o.DomEvent._isExternalTarget(r,t)||(n=[this]),n},_handleDOMEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e="keypress"===t.type&&13===t.keyCode?"click":t.type;"mousedown"===e&&o.DomUtil.preventOutline(t.target||t.srcElement),this._fireDOMEvent(t,e)}},_fireDOMEvent:function(t,e,i){if("click"===t.type){var n=o.Util.extend({},t);n.type="preclick",this._fireDOMEvent(n,n.type,i)}if(!t._stopped&&(i=(i||[]).concat(this._findEventTargets(t,e)),i.length)){var s=i[0];"contextmenu"===e&&s.listens(e,!0)&&o.DomEvent.preventDefault(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s instanceof o.Marker;r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h<i.length;h++)if(i[h].fire(e,r,!0),r.originalEvent._stopped||i[h].options.nonBubblingEvents&&o.Util.indexOf(i[h].options.nonBubblingEvents,e)!==-1)return}},_draggableMoved:function(t){return t=t.dragging&&t.dragging.enabled()?t:this,t.dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,e=this._handlers.length;t<e;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,{target:this}):this.on("load",t,e),this},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)||new o.Point(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,e){var n=t&&e!==i?this._getNewPixelOrigin(t,e):this.getPixelOrigin();return n.subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewPixelOrigin(i,e);return this.project(t,e)._subtract(n)},_latLngBoundsToNewLayerBounds:function(t,e,i){var n=this._getNewPixelOrigin(i,e);return o.bounds([this.project(t.getSouthWest(),e)._subtract(n),this.project(t.getNorthWest(),e)._subtract(n),this.project(t.getSouthEast(),e)._subtract(n),this.project(t.getNorthEast(),e)._subtract(n)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,e,i){if(!i)return t;var n=this.project(t,e),s=this.getSize().divideBy(2),r=new o.Bounds(n.subtract(s),n.add(s)),a=this._getBoundsOffset(r,i,e);return a.round().equals([0,0])?t:this.unproject(n.add(a),e)},_limitOffset:function(t,e){if(!e)return t;var i=this.getPixelBounds(),n=new o.Bounds(i.min.add(t),i.max.add(t));return t.add(this._getBoundsOffset(n,e))},_getBoundsOffset:function(t,e,i){var n=o.bounds(this.project(e.getNorthEast(),i),this.project(e.getSouthWest(),i)),s=n.min.subtract(t.min),r=n.max.subtract(t.max),a=this._rebound(s.x,-r.x),h=this._rebound(s.y,-r.y);return new o.Point(a,h)},_rebound:function(t,e){return t+e>0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom(),n=o.Browser.any3d?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(e,Math.min(i,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return!((e&&e.animate)!==!0&&!this.getSize().contains(i))&&(this.panBy(i,e),!0)},_createAnimProxy:function(){var t=this._proxy=o.DomUtil.create("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(e){var i=o.DomUtil.TRANSFORM,n=t.style[i];o.DomUtil.setTransform(t,this.project(e.center,e.zoom),this.getZoomScale(e.zoom,1)),n===t.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var e=this.getCenter(),i=this.getZoom();o.DomUtil.setTransform(t,this.project(e,i),this.getZoomScale(i,1))},this)},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),s=this._getCenterOffset(t)._divideBy(1-1/n);return!(i.animate!==!0&&!this.getSize().contains(s))&&(o.Util.requestAnimFrame(function(){this._moveStart(!0)._animateZoom(t,e,!0)},this),!0)},_animateZoom:function(t,e,i,n){i&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=e,o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:e,noUpdate:n}),setTimeout(o.bind(this._onZoomTransitionEnd,this),250)},_onZoomTransitionEnd:function(){this._animatingZoom&&(o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),o.Util.requestAnimFrame(function(){this._moveEnd(!0)},this))}}),o.map=function(t,e){return new o.Map(t,e)},o.Layer=o.Evented.extend({options:{pane:"overlayPane",nonBubblingEvents:[],attribution:null},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[o.stamp(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[o.stamp(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var e=t.target;if(e.hasLayer(this)){if(this._map=e,this._zoomAnimated=e._zoomAnimated,this.getEvents){var i=this.getEvents();e.on(i,this),this.once("remove",function(){e.off(i,this)},this)}this.onAdd(e),this.getAttribution&&e.attributionControl&&e.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),e.fire("layeradd",{layer:this})}}}),o.Map.include({addLayer:function(t){var e=o.stamp(t);return this._layers[e]?this:(this._layers[e]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var e=o.stamp(t);return this._layers[e]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[e],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&o.stamp(t)in this._layers},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;e<i;e++)this.addLayer(t[e])},_addZoomLimit:function(t){!isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[o.stamp(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){var e=o.stamp(t);this._zoomBoundLayers[e]&&(delete this._zoomBoundLayers[e],this._updateZoomLevels())},_updateZoomLevels:function(){var t=1/0,e=-(1/0),n=this._getZoomSpan();for(var o in this._zoomBoundLayers){var s=this._zoomBoundLayers[o].options;t=s.minZoom===i?t:Math.min(t,s.minZoom),e=s.maxZoom===i?e:Math.max(e,s.maxZoom)}this._layersMaxZoom=e===-(1/0)?i:e,this._layersMinZoom=t===1/0?i:t,n!==this._getZoomSpan()&&this.fire("zoomlevelschange"),this.options.maxZoom===i&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),this.options.minZoom===i&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}});var r="_leaflet_events";o.DomEvent={on:function(t,e,i,n){if("object"==typeof e)for(var s in e)this._on(t,s,e[s],i);else{e=o.Util.splitWords(e);for(var r=0,a=e.length;r<a;r++)this._on(t,e[r],i,n)}return this},off:function(t,e,i,n){if("object"==typeof e)for(var s in e)this._off(t,s,e[s],i);else{e=o.Util.splitWords(e);for(var r=0,a=e.length;r<a;r++)this._off(t,e[r],i,n)}return this},_on:function(e,i,n,s){var a=i+o.stamp(n)+(s?"_"+o.stamp(s):"");if(e[r]&&e[r][a])return this;var h=function(i){return n.call(s||e,i||t.event)},l=h;return o.Browser.pointer&&0===i.indexOf("touch")?this.addPointerListener(e,i,h,a):!o.Browser.touch||"dblclick"!==i||!this.addDoubleTapListener||o.Browser.pointer&&o.Browser.chrome?"addEventListener"in e?"mousewheel"===i?e.addEventListener("onwheel"in e?"wheel":"mousewheel",h,!1):"mouseenter"===i||"mouseleave"===i?(h=function(i){i=i||t.event,o.DomEvent._isExternalTarget(e,i)&&l(i)},e.addEventListener("mouseenter"===i?"mouseover":"mouseout",h,!1)):("click"===i&&o.Browser.android&&(h=function(t){return o.DomEvent._filterClick(t,l)}),e.addEventListener(i,h,!1)):"attachEvent"in e&&e.attachEvent("on"+i,h):this.addDoubleTapListener(e,h,a),e[r]=e[r]||{},e[r][a]=h,this},_off:function(t,e,i,n){var s=e+o.stamp(i)+(n?"_"+o.stamp(n):""),a=t[r]&&t[r][s];return a?(o.Browser.pointer&&0===e.indexOf("touch")?this.removePointerListener(t,e,s):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,s):"removeEventListener"in t?"mousewheel"===e?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",a,!1):t.removeEventListener("mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[r][s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,o.DomEvent._skipped(t),this},disableScrollPropagation:function(t){return o.DomEvent.on(t,"mousewheel",o.DomEvent.stopPropagation)},disableClickPropagation:function(t){var e=o.DomEvent.stopPropagation;return o.DomEvent.on(t,o.Draggable.START.join(" "),e),o.DomEvent.on(t,{click:o.DomEvent._fakeStop,dblclick:e})},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,e){if(!e)return new o.Point(t.clientX,t.clientY);var i=e.getBoundingClientRect();return new o.Point(t.clientX-i.left-e.clientLeft,t.clientY-i.top-e.clientTop)},_wheelPxFactor:o.Browser.win&&o.Browser.chrome?2:o.Browser.gecko?t.devicePixelRatio:1,getWheelDelta:function(t){return o.Browser.edge?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/o.DomEvent._wheelPxFactor:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_isExternalTarget:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(t){return!1}return i!==t},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?void o.DomEvent.stop(t):(o.DomEvent._lastClick=i,void e(t))}},o.DomEvent.addListener=o.DomEvent.on,o.DomEvent.removeListener=o.DomEvent.off,o.PosAnimation=o.Evented.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(t){var e=+new Date-this._startTime,i=1e3*this._duration;e<i?this._runFrame(this._easeOut(e/i),t):(this._runFrame(1),this._complete())},_runFrame:function(t,e){var i=this._startPos.add(this._offset.multiplyBy(t));e&&i._round(),o.DomUtil.setPosition(this._el,i),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Projection.Mercator={R:6378137,R_MINOR:6356752.314245179,bounds:o.bounds([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var e=Math.PI/180,i=this.R,n=t.lat*e,s=this.R_MINOR/i,r=Math.sqrt(1-s*s),a=r*Math.sin(n),h=Math.tan(Math.PI/4-n/2)/Math.pow((1-a)/(1+a),r/2);return n=-i*Math.log(Math.max(h,1e-10)),new o.Point(t.lng*e*i,n)},unproject:function(t){for(var e,i=180/Math.PI,n=this.R,s=this.R_MINOR/n,r=Math.sqrt(1-s*s),a=Math.exp(-t.y/n),h=Math.PI/2-2*Math.atan(a),l=0,u=.1;l<15&&Math.abs(u)>1e-7;l++)e=r*Math.sin(h),e=Math.pow((1-e)/(1+e),r/2),u=Math.PI/2-2*Math.atan(a*e)-h,h+=u;return new o.LatLng(h*i,t.x*i/n)}},o.CRS.EPSG3395=o.extend({},o.CRS.Earth,{code:"EPSG:3395",projection:o.Projection.Mercator,transformation:function(){var t=.5/(Math.PI*o.Projection.Mercator.R);return new o.Transformation(t,.5,-t,.5)}()}),o.GridLayer=o.Layer.extend({options:{tileSize:256,opacity:1,updateWhenIdle:o.Browser.mobile,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:i,noWrap:!1,pane:"tilePane",className:"",keepBuffer:2},initialize:function(t){o.setOptions(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView(),this._update()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),o.DomUtil.remove(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=null},bringToFront:function(){return this._map&&(o.DomUtil.toFront(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(o.DomUtil.toBack(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){return this._map&&(this._removeAllTiles(),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=o.Util.throttle(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return e.createElement("div")},getTileSize:function(){var t=this.options.tileSize;return t instanceof o.Point?t:new o.Point(t,t)},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var e,i=this.getPane().children,n=-t(-(1/0),1/0),o=0,s=i.length;o<s;o++)e=i[o].style.zIndex,i[o]!==this._container&&e&&(n=t(n,+e));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!o.Browser.ielt9){o.DomUtil.setOpacity(this._container,this.options.opacity);var t=+new Date,e=!1,i=!1;for(var n in this._tiles){var s=this._tiles[n];if(s.current&&s.loaded){var r=Math.min(1,(t-s.loaded)/200);o.DomUtil.setOpacity(s.el,r),r<1?e=!0:(s.active&&(i=!0),s.active=!0)}}i&&!this._noPrune&&this._pruneTiles(),e&&(o.Util.cancelAnimFrame(this._fadeFrame),this._fadeFrame=o.Util.requestAnimFrame(this._updateOpacity,this))}},_initContainer:function(){this._container||(this._container=o.DomUtil.create("div","leaflet-layer "+(this.options.className||"")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,e=this.options.maxZoom;if(t===i)return i;for(var n in this._levels)this._levels[n].el.children.length||n===t?this._levels[n].el.style.zIndex=e-Math.abs(t-n):(o.DomUtil.remove(this._levels[n].el),this._removeTilesAtZoom(n),delete this._levels[n]);var s=this._levels[t],r=this._map;return s||(s=this._levels[t]={},s.el=o.DomUtil.create("div","leaflet-tile-container leaflet-zoom-animated",this._container),s.el.style.zIndex=e,s.origin=r.project(r.unproject(r.getPixelOrigin()),t).round(),s.zoom=t,this._setZoomTransform(s,r.getCenter(),r.getZoom()),o.Util.falseFn(s.el.offsetWidth)),this._level=s,s},_pruneTiles:function(){if(this._map){var t,e,i=this._map.getZoom();if(i>this.options.maxZoom||i<this.options.minZoom)return void this._removeAllTiles();for(t in this._tiles)e=this._tiles[t],e.retain=e.current;for(t in this._tiles)if(e=this._tiles[t],e.current&&!e.active){var n=e.coords;this._retainParent(n.x,n.y,n.z,n.z-5)||this._retainChildren(n.x,n.y,n.z,n.z+2)}for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}},_removeTilesAtZoom:function(t){for(var e in this._tiles)this._tiles[e].coords.z===t&&this._removeTile(e)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)o.DomUtil.remove(this._levels[t].el),delete this._levels[t];this._removeAllTiles(),this._tileZoom=null},_retainParent:function(t,e,i,n){var s=Math.floor(t/2),r=Math.floor(e/2),a=i-1,h=new o.Point(+s,+r);h.z=+a;var l=this._tileCoordsToKey(h),u=this._tiles[l];return u&&u.active?(u.retain=!0,!0):(u&&u.loaded&&(u.retain=!0),a>n&&this._retainParent(s,r,a,n))},_retainChildren:function(t,e,i,n){for(var s=2*t;s<2*t+2;s++)for(var r=2*e;r<2*e+2;r++){var a=new o.Point(s,r);a.z=i+1;var h=this._tileCoordsToKey(a),l=this._tiles[h];l&&l.active?l.retain=!0:(l&&l.loaded&&(l.retain=!0),i+1<n&&this._retainChildren(s,r,i+1,n))}},_resetView:function(t){var e=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),e,e)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_setView:function(t,e,n,o){var s=Math.round(e);(this.options.maxZoom!==i&&s>this.options.maxZoom||this.options.minZoom!==i&&s<this.options.minZoom)&&(s=i);var r=this.options.updateWhenZooming&&s!==this._tileZoom;o&&!r||(this._tileZoom=s,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),s!==i&&this._update(t),n||this._pruneTiles(),this._noPrune=!!n),this._setZoomTransforms(t,e)},_setZoomTransforms:function(t,e){for(var i in this._levels)this._setZoomTransform(this._levels[i],t,e)},_setZoomTransform:function(t,e,i){var n=this._map.getZoomScale(i,t.zoom),s=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(e,i)).round();o.Browser.any3d?o.DomUtil.setTransform(t.el,s,n):o.DomUtil.setPosition(t.el,s)},_resetGrid:function(){var t=this._map,e=t.options.crs,i=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=e.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,e.wrapLng[0]],n).x/i.x),Math.ceil(t.project([0,e.wrapLng[1]],n).x/i.y)],this._wrapY=e.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([e.wrapLat[0],0],n).y/i.x),Math.ceil(t.project([e.wrapLat[1],0],n).y/i.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var e=this._map,i=e._animatingZoom?Math.max(e._animateToZoom,e.getZoom()):e.getZoom(),n=e.getZoomScale(i,this._tileZoom),s=e.project(t,this._tileZoom).floor(),r=e.getSize().divideBy(2*n);return new o.Bounds(s.subtract(r),s.add(r))},_update:function(t){var n=this._map;if(n){var s=n.getZoom();if(t===i&&(t=n.getCenter()),this._tileZoom!==i){var r=this._getTiledPixelBounds(t),a=this._pxBoundsToTileRange(r),h=a.getCenter(),l=[],u=this.options.keepBuffer,c=new o.Bounds(a.getBottomLeft().subtract([u,-u]),a.getTopRight().add([u,-u]));for(var d in this._tiles){var _=this._tiles[d].coords;_.z===this._tileZoom&&c.contains(o.point(_.x,_.y))||(this._tiles[d].current=!1)}if(Math.abs(s-this._tileZoom)>1)return void this._setView(t,s);for(var m=a.min.y;m<=a.max.y;m++)for(var p=a.min.x;p<=a.max.x;p++){var f=new o.Point(p,m);if(f.z=this._tileZoom,this._isValidTile(f)){var g=this._tiles[this._tileCoordsToKey(f)];g?g.current=!0:l.push(f)}}if(l.sort(function(t,e){return t.distanceTo(h)-e.distanceTo(h)}),0!==l.length){this._loading||(this._loading=!0,this.fire("loading"));var v=e.createDocumentFragment();for(p=0;p<l.length;p++)this._addTile(l[p],v);this._level.el.appendChild(v)}}}},_isValidTile:function(t){var e=this._map.options.crs;if(!e.infinite){var i=this._globalTileRange;if(!e.wrapLng&&(t.x<i.min.x||t.x>i.max.x)||!e.wrapLat&&(t.y<i.min.y||t.y>i.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return o.latLngBounds(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToBounds:function(t){var e=this._map,i=this.getTileSize(),n=t.scaleBy(i),s=n.add(i),r=e.unproject(n,t.z),a=e.unproject(s,t.z),h=new o.LatLngBounds(r,a);return this.options.noWrap||e.wrapLatLngBounds(h),h},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var e=t.split(":"),i=new o.Point(+e[0],+e[1]);return i.z=+e[2],i},_removeTile:function(t){var e=this._tiles[t];e&&(o.DomUtil.remove(e.el),delete this._tiles[t],this.fire("tileunload",{tile:e.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){o.DomUtil.addClass(t,"leaflet-tile");var e=this.getTileSize();t.style.width=e.x+"px",t.style.height=e.y+"px",t.onselectstart=o.Util.falseFn,t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity<1&&o.DomUtil.setOpacity(t,this.options.opacity),o.Browser.android&&!o.Browser.android23&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,e){var i=this._getTilePos(t),n=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),o.bind(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&o.Util.requestAnimFrame(o.bind(this._tileReady,this,t,null,s)),o.DomUtil.setPosition(s,i),this._tiles[n]={el:s,coords:t,current:!0},e.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,e,i){if(this._map){e&&this.fire("tileerror",{error:e,tile:i,coords:t});var n=this._tileCoordsToKey(t);i=this._tiles[n],i&&(i.loaded=+new Date,this._map._fadeAnimated?(o.DomUtil.setOpacity(i.el,0),o.Util.cancelAnimFrame(this._fadeFrame),this._fadeFrame=o.Util.requestAnimFrame(this._updateOpacity,this)):(i.active=!0,this._pruneTiles()),e||(o.DomUtil.addClass(i.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:i.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),o.Browser.ielt9||!this._map._fadeAnimated?o.Util.requestAnimFrame(this._pruneTiles,this):setTimeout(o.bind(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var e=new o.Point(this._wrapX?o.Util.wrapNum(t.x,this._wrapX):t.x,this._wrapY?o.Util.wrapNum(t.y,this._wrapY):t.y);return e.z=t.z,e},_pxBoundsToTileRange:function(t){var e=this.getTileSize();return new o.Bounds(t.min.unscaleBy(e).floor(),t.max.unscaleBy(e).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),o.gridLayer=function(t){return new o.GridLayer(t)},o.TileLayer=o.GridLayer.extend({options:{minZoom:0,maxZoom:18,maxNativeZoom:null,minNativeZoom:null,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,e){this._url=t,e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomReverse?(e.zoomOffset--,e.minZoom++):(e.zoomOffset++,e.maxZoom--),e.minZoom=Math.max(0,e.minZoom)),"string"==typeof e.subdomains&&(e.subdomains=e.subdomains.split("")),o.Browser.android||this.on("tileunload",this._onTileRemove)},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},createTile:function(t,i){var n=e.createElement("img");return o.DomEvent.on(n,"load",o.bind(this._tileOnLoad,this,i,n)),o.DomEvent.on(n,"error",o.bind(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:o.Browser.retina?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var i=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=i),e["-y"]=i}return o.Util.template(this._url,o.extend(e,this.options))},_tileOnLoad:function(t,e){o.Browser.ielt9?setTimeout(o.bind(t,this,null,e),0):t(null,e)},_tileOnError:function(t,e,i){var n=this.options.errorTileUrl;n&&e.src!==n&&(e.src=n),t(i,e)},getTileSize:function(){var t=this._map,e=o.GridLayer.prototype.getTileSize.call(this),i=this._tileZoom+this.options.zoomOffset,n=this.options.minNativeZoom,s=this.options.maxNativeZoom;return null!==n&&i<n?e.divideBy(t.getZoomScale(n,i)).round():null!==s&&i>s?e.divideBy(t.getZoomScale(s,i)).round():e},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,e=this.options.maxZoom,i=this.options.zoomReverse,n=this.options.zoomOffset,o=this.options.minNativeZoom,s=this.options.maxNativeZoom;return i&&(t=e-t),t+=n,null!==o&&t<o?o:null!==s&&t>s?s:t},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_abortLoading:function(){var t,e;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&(e=this._tiles[t].el,e.onload=o.Util.falseFn,e.onerror=o.Util.falseFn,e.complete||(e.src=o.Util.emptyImageUrl,o.DomUtil.remove(e)))}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams);for(var n in e)n in this.options||(i[n]=e[n]);e=o.setOptions(this,e),i.width=i.height=e.tileSize*(e.detectRetina&&o.Browser.retina?2:1),this.wmsParams=i},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._tileCoordsToBounds(t),i=this._crs.project(e.getNorthWest()),n=this._crs.project(e.getSouthEast()),s=(this._wmsVersion>=1.3&&this._crs===o.CRS.EPSG4326?[n.y,i.x,i.y,n.x]:[i.x,n.y,n.x,i.y]).join(","),r=o.TileLayer.prototype.getTileUrl.call(this,t);return r+o.Util.getParamString(this.wmsParams,r,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+s},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.ImageOverlay=o.Layer.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(o.DomUtil.addClass(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){o.DomUtil.remove(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&o.DomUtil.toFront(this._image),this},bringToBack:function(){return this._map&&o.DomUtil.toBack(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=t,this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t=this._image=o.DomUtil.create("img","leaflet-image-layer "+(this._zoomAnimated?"leaflet-zoom-animated":""));t.onselectstart=o.Util.falseFn,t.onmousemove=o.Util.falseFn,t.onload=o.bind(this.fire,this,"load"),this.options.crossOrigin&&(t.crossOrigin=""),t.src=this._url,t.alt=this.options.alt},_animateZoom:function(t){var e=this._map.getZoomScale(t.zoom),i=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;o.DomUtil.setTransform(this._image,i,e)},_reset:function(){var t=this._image,e=new o.Bounds(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),i=e.getSize();
o.DomUtil.setPosition(t,e.min),t.style.width=i.x+"px",t.style.height=i.y+"px"},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(i,e&&"IMG"===e.tagName?e:null);return this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i=this.options,n=i[e+"Size"];"number"==typeof n&&(n=[n,n]);var s=o.point(n),r=o.point("shadow"===e&&i.shadowAnchor||i.iconAnchor||s&&s.divideBy(2,!0));t.className="leaflet-marker-"+e+" "+(i.className||""),r&&(t.style.marginLeft=-r.x+"px",t.style.marginTop=-r.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return i=i||e.createElement("img"),i.src=t,i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]||this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconUrl:"marker-icon.png",iconRetinaUrl:"marker-icon-2x.png",shadowUrl:"marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return o.Icon.Default.imagePath||(o.Icon.Default.imagePath=this._detectIconPath()),(this.options.imagePath||o.Icon.Default.imagePath)+o.Icon.prototype._getIconUrl.call(this,t)},_detectIconPath:function(){var t=o.DomUtil.create("div","leaflet-default-icon-path",e.body),i=o.DomUtil.getStyle(t,"background-image")||o.DomUtil.getStyle(t,"backgroundImage");return e.body.removeChild(t),0===i.indexOf("url")?i.replace(/^url\([\"\']?/,"").replace(/marker-icon\.png[\"\']?\)$/,""):""}}),o.Marker=o.Layer.extend({options:{icon:new o.Icon.Default,interactive:!0,draggable:!1,keyboard:!0,title:"",alt:"",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:"markerPane",nonBubblingEvents:["click","dblclick","mouseover","mouseout","contextmenu"]},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on("zoomanim",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),this._zoomAnimated&&t.off("zoomanim",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var e=this._latlng;return this._latlng=o.latLng(t),this.update(),this.fire("move",{oldLatLng:e,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e="leaflet-zoom-"+(this._zoomAnimated?"animated":"hide"),i=t.icon.createIcon(this._icon),n=!1;i!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(i.title=t.title),t.alt&&(i.alt=t.alt)),o.DomUtil.addClass(i,e),t.keyboard&&(i.tabIndex="0"),this._icon=i,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex});var s=t.icon.createShadow(this._shadow),r=!1;s!==this._shadow&&(this._removeShadow(),r=!0),s&&(o.DomUtil.addClass(s,e),s.alt=""),this._shadow=s,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),s&&r&&this.getPane("shadowPane").appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),o.DomUtil.remove(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&o.DomUtil.remove(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(e)},_initInteraction:function(){if(this.options.interactive&&(o.DomUtil.addClass(this._icon,"leaflet-interactive"),this.addInteractiveTarget(this._icon),o.Handler.MarkerDrag)){var t=this.options.draggable;this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new o.Handler.MarkerDrag(this),t&&this.dragging.enable()}},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;o.DomUtil.setOpacity(this._icon,t),this._shadow&&o.DomUtil.setOpacity(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor||[0,0]},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor||[0,0]}}),o.marker=function(t,e){return new o.Marker(t,e)},o.DivIcon=o.Icon.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:"leaflet-div-icon"},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:e.createElement("div"),n=this.options;if(i.innerHTML=n.html!==!1?n.html:"",n.bgPos){var s=o.point(n.bgPos);i.style.backgroundPosition=-s.x+"px "+-s.y+"px"}return this._setIconStyles(i,"icon"),i},createShadow:function(){return null}}),o.divIcon=function(t){return new o.DivIcon(t)},o.DivOverlay=o.Layer.extend({options:{offset:[0,7],className:"",pane:"popupPane"},initialize:function(t,e){o.setOptions(this,t),this._source=e},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&o.DomUtil.setOpacity(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&o.DomUtil.setOpacity(this._container,1),this.bringToFront()},onRemove:function(t){t._fadeAnimated?(o.DomUtil.setOpacity(this._container,0),this._removeTimeout=setTimeout(o.bind(o.DomUtil.remove,o.DomUtil,this._container),200)):o.DomUtil.remove(this._container)},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&o.DomUtil.toFront(this._container),this},bringToBack:function(){return this._map&&o.DomUtil.toBack(this._container),this},_updateContent:function(){if(this._content){var t=this._contentNode,e="function"==typeof this._content?this._content(this._source||this):this._content;if("string"==typeof e)t.innerHTML=e;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(e)}this.fire("contentupdate")}},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=o.point(this.options.offset),i=this._getAnchor();this._zoomAnimated?o.DomUtil.setPosition(this._container,t.add(i)):e=e.add(t).add(i);var n=this._containerBottom=-e.y,s=this._containerLeft=-Math.round(this._containerWidth/2)+e.x;this._container.style.bottom=n+"px",this._container.style.left=s+"px"}},_getAnchor:function(){return[0,0]}}),o.Popup=o.DivOverlay.extend({options:{maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,className:""},openOn:function(t){return t.openPopup(this),this},onAdd:function(t){o.DivOverlay.prototype.onAdd.call(this,t),t.fire("popupopen",{popup:this}),this._source&&(this._source.fire("popupopen",{popup:this},!0),this._source instanceof o.Path||this._source.on("preclick",o.DomEvent.stopPropagation))},onRemove:function(t){o.DivOverlay.prototype.onRemove.call(this,t),t.fire("popupclose",{popup:this}),this._source&&(this._source.fire("popupclose",{popup:this},!0),this._source instanceof o.Path||this._source.off("preclick",o.DomEvent.stopPropagation))},getEvents:function(){var t=o.DivOverlay.prototype.getEvents.call(this);return("closeOnClick"in this.options?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t="leaflet-popup",e=this._container=o.DomUtil.create("div",t+" "+(this.options.className||"")+" leaflet-zoom-animated");if(this.options.closeButton){var i=this._closeButton=o.DomUtil.create("a",t+"-close-button",e);i.href="#close",i.innerHTML="×",o.DomEvent.on(i,"click",this._onCloseButtonClick,this)}var n=this._wrapper=o.DomUtil.create("div",t+"-content-wrapper",e);this._contentNode=o.DomUtil.create("div",t+"-content",n),o.DomEvent.disableClickPropagation(n).disableScrollPropagation(this._contentNode).on(n,"contextmenu",o.DomEvent.stopPropagation),this._tipContainer=o.DomUtil.create("div",t+"-tip-container",e),this._tip=o.DomUtil.create("div",t+"-tip",this._tipContainer)},_updateLayout:function(){var t=this._contentNode,e=t.style;e.width="",e.whiteSpace="nowrap";var i=t.offsetWidth;i=Math.min(i,this.options.maxWidth),i=Math.max(i,this.options.minWidth),e.width=i+1+"px",e.whiteSpace="",e.height="";var n=t.offsetHeight,s=this.options.maxHeight,r="leaflet-popup-scrolled";s&&n>s?(e.height=s+"px",o.DomUtil.addClass(t,r)):o.DomUtil.removeClass(t,r),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),i=this._getAnchor();o.DomUtil.setPosition(this._container,e.add(i))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,e=parseInt(o.DomUtil.getStyle(this._container,"marginBottom"),10)||0,i=this._container.offsetHeight+e,n=this._containerWidth,s=new o.Point(this._containerLeft,-i-this._containerBottom);s._add(o.DomUtil.getPosition(this._container));var r=t.layerPointToContainerPoint(s),a=o.point(this.options.autoPanPadding),h=o.point(this.options.autoPanPaddingTopLeft||a),l=o.point(this.options.autoPanPaddingBottomRight||a),u=t.getSize(),c=0,d=0;r.x+n+l.x>u.x&&(c=r.x+n-u.x+l.x),r.x-c-h.x<0&&(c=r.x-h.x),r.y+i+l.y>u.y&&(d=r.y+i-u.y+l.y),r.y-d-h.y<0&&(d=r.y-h.y),(c||d)&&t.fire("autopanstart").panBy([c,d])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)},_getAnchor:function(){return o.point(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.mergeOptions({closePopupOnClick:!0}),o.Map.include({openPopup:function(t,e,i){return t instanceof o.Popup||(t=new o.Popup(i).setContent(t)),e&&t.setLatLng(e),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),o.Layer.include({bindPopup:function(t,e){return t instanceof o.Popup?(o.setOptions(t,e),this._popup=t,t._source=this):(this._popup&&!e||(this._popup=new o.Popup(e,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,e){if(t instanceof o.Layer||(e=t,t=this),t instanceof o.FeatureGroup)for(var i in this._layers){t=this._layers[i];break}return e||(e=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,e)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var e=t.layer||t.target;if(this._popup&&this._map)return o.DomEvent.stop(t),e instanceof o.Path?void this.openPopup(t.layer||t.target,t.latlng):void(this._map.hasLayer(this._popup)&&this._popup._source===e?this.closePopup():this.openPopup(e,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.Tooltip=o.DivOverlay.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){o.DivOverlay.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){o.DivOverlay.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=o.DivOverlay.prototype.getEvents.call(this);return o.Browser.touch&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip",e=t+" "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=o.DomUtil.create("div",e)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var e=this._map,i=this._container,n=e.latLngToContainerPoint(e.getCenter()),s=e.layerPointToContainerPoint(t),r=this.options.direction,a=i.offsetWidth,h=i.offsetHeight,l=o.point(this.options.offset),u=this._getAnchor();"top"===r?t=t.add(o.point(-a/2+l.x,-h+l.y+u.y,!0)):"bottom"===r?t=t.subtract(o.point(a/2-l.x,-l.y,!0)):"center"===r?t=t.subtract(o.point(a/2+l.x,h/2-u.y+l.y,!0)):"right"===r||"auto"===r&&s.x<n.x?(r="right",t=t.add(o.point(l.x+u.x,u.y-h/2+l.y,!0))):(r="left",t=t.subtract(o.point(a+u.x-l.x,h/2-u.y-l.y,!0))),o.DomUtil.removeClass(i,"leaflet-tooltip-right"),o.DomUtil.removeClass(i,"leaflet-tooltip-left"),o.DomUtil.removeClass(i,"leaflet-tooltip-top"),o.DomUtil.removeClass(i,"leaflet-tooltip-bottom"),o.DomUtil.addClass(i,"leaflet-tooltip-"+r),o.DomUtil.setPosition(i,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&o.DomUtil.setOpacity(this._container,t)},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(e)},_getAnchor:function(){return o.point(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}}),o.tooltip=function(t,e){return new o.Tooltip(t,e)},o.Map.include({openTooltip:function(t,e,i){return t instanceof o.Tooltip||(t=new o.Tooltip(i).setContent(t)),e&&t.setLatLng(e),this.hasLayer(t)?this:this.addLayer(t)},closeTooltip:function(t){return t&&this.removeLayer(t),this}}),o.Layer.include({bindTooltip:function(t,e){return t instanceof o.Tooltip?(o.setOptions(t,e),this._tooltip=t,t._source=this):(this._tooltip&&!e||(this._tooltip=o.tooltip(e,this)),this._tooltip.setContent(t)),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){if(t||!this._tooltipHandlersAdded){var e=t?"off":"on",i={remove:this.closeTooltip,move:this._moveTooltip};this._tooltip.options.permanent?i.add=this._openTooltip:(i.mouseover=this._openTooltip,i.mouseout=this.closeTooltip,this._tooltip.options.sticky&&(i.mousemove=this._moveTooltip),o.Browser.touch&&(i.click=this._openTooltip)),this[e](i),this._tooltipHandlersAdded=!t}},openTooltip:function(t,e){if(t instanceof o.Layer||(e=t,t=this),t instanceof o.FeatureGroup)for(var i in this._layers){t=this._layers[i];break}return e||(e=t.getCenter?t.getCenter():t.getLatLng()),this._tooltip&&this._map&&(this._tooltip._source=t,this._tooltip.update(),this._map.openTooltip(this._tooltip,e),this._tooltip.options.interactive&&this._tooltip._container&&(o.DomUtil.addClass(this._tooltip._container,"leaflet-clickable"),this.addInteractiveTarget(this._tooltip._container))),this},closeTooltip:function(){return this._tooltip&&(this._tooltip._close(),this._tooltip.options.interactive&&this._tooltip._container&&(o.DomUtil.removeClass(this._tooltip._container,"leaflet-clickable"),this.removeInteractiveTarget(this._tooltip._container))),this},toggleTooltip:function(t){return this._tooltip&&(this._tooltip._map?this.closeTooltip():this.openTooltip(t)),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_openTooltip:function(t){var e=t.layer||t.target;this._tooltip&&this._map&&this.openTooltip(e,this._tooltip.options.sticky?t.latlng:i)},_moveTooltip:function(t){var e,i,n=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(e=this._map.mouseEventToContainerPoint(t.originalEvent),i=this._map.containerPointToLayerPoint(e),n=this._map.layerPointToLatLng(i)),this._tooltip.setLatLng(n)}}),o.LayerGroup=o.Layer.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;e<i;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return!!t&&(t in this._layers||this.getLayerId(t)in this._layers)},clearLayers:function(){for(var t in this._layers)this.removeLayer(this._layers[t]);return this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){for(var e in this._layers)t.addLayer(this._layers[e])},onRemove:function(t){for(var e in this._layers)t.removeLayer(this._layers[e])},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),o.LayerGroup.prototype.addLayer.call(this,t),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.removeEventParent(this),o.LayerGroup.prototype.removeLayer.call(this,t),this.fire("layerremove",{layer:t})):this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new o.LatLngBounds;for(var e in this._layers){var i=this._layers[e];t.extend(i.getBounds?i.getBounds():i.getLatLng())}return t}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Renderer=o.Layer.extend({options:{padding:.1},initialize:function(t){o.setOptions(this,t),o.stamp(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&o.DomUtil.addClass(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){o.DomUtil.remove(this._container),this.off("update",this._updatePaths,this)},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,e){var i=this._map.getZoomScale(e,this._zoom),n=o.DomUtil.getPosition(this._container),s=this._map.getSize().multiplyBy(.5+this.options.padding),r=this._map.project(this._center,e),a=this._map.project(t,e),h=a.subtract(r),l=s.multiplyBy(-i).add(n).add(s).subtract(h);o.Browser.any3d?o.DomUtil.setTransform(this._container,l,i):o.DomUtil.setPosition(this._container,l)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,e=this._map.getSize(),i=this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round();this._bounds=new o.Bounds(i,i.add(e.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),o.Map.include({getRenderer:function(t){var e=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return e||(e=this._renderer=this.options.preferCanvas&&o.canvas()||o.svg()),this.hasLayer(e)||this.addLayer(e),e},_getPaneRenderer:function(t){if("overlayPane"===t||t===i)return!1;var e=this._paneRenderers[t];return e===i&&(e=o.SVG&&o.svg({pane:t})||o.Canvas&&o.canvas({pane:t}),this._paneRenderers[t]=e),e}}),o.Path=o.Layer.extend({options:{stroke:!0,color:"#3388ff",weight:3,opacity:1,lineCap:"round",lineJoin:"round",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:"evenodd",interactive:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return o.setOptions(this,t),this._renderer&&this._renderer._updateStyle(this),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+(o.Browser.touch?10:0)}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var r,a=[];for(r=0;r<n;r++)s[r]&&a.push(t[r]);return a},_simplifyDPStep:function(t,e,i,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)a=this._sqClosestPointOnSegment(t[r],t[n],t[o],!0),a>h&&(s=r,h=a);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;n<s;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return o<s-1&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n,o){var s,r,a,h=n?this._lastCode:this._getBitCode(t,i),l=this._getBitCode(e,i);for(this._lastCode=l;;){if(!(h|l))return[t,e];if(h&l)return!1;s=h||l,r=this._getEdgeIntersection(t,e,s,i,o),a=this._getBitCode(r,i),s===h?(t=r,h=a):(e=r,l=a)}},_getEdgeIntersection:function(t,e,i,n,s){var r,a,h=e.x-t.x,l=e.y-t.y,u=n.min,c=n.max;return 8&i?(r=t.x+h*(c.y-t.y)/l,a=c.y):4&i?(r=t.x+h*(u.y-t.y)/l,a=u.y):2&i?(r=c.x,a=t.y+l*(c.x-t.x)/h):1&i&&(r=u.x,a=t.y+l*(u.x-t.x)/h),new o.Point(r,a,s)},_getBitCode:function(t,e){var i=0;return t.x<e.min.x?i|=1:t.x>e.max.x&&(i|=2),t.y<e.min.y?i|=4:t.y>e.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,r=e.x,a=e.y,h=i.x-r,l=i.y-a,u=h*h+l*l;return u>0&&(s=((t.x-r)*h+(t.y-a)*l)/u,s>1?(r=i.x,a=i.y):s>0&&(r+=h*s,a+=l*s)),h=t.x-r,l=t.y-a,n?h*h+l*l:new o.Point(r,a)}},o.Polyline=o.Path.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,e){o.setOptions(this,e),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var e,i,n=1/0,s=null,r=o.LineUtil._sqClosestPointOnSegment,a=0,h=this._parts.length;a<h;a++)for(var l=this._parts[a],u=1,c=l.length;u<c;u++){e=l[u-1],i=l[u];var d=r(t,e,i,!0);d<n&&(n=d,s=r(t,e,i))}return s&&(s.distance=Math.sqrt(n)),s},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,e,i,n,o,s,r,a=this._rings[0],h=a.length;if(!h)return null;for(t=0,e=0;t<h-1;t++)e+=a[t].distanceTo(a[t+1])/2;if(0===e)return this._map.layerPointToLatLng(a[0]);for(t=0,n=0;t<h-1;t++)if(o=a[t],s=a[t+1],i=o.distanceTo(s),n+=i,n>e)return r=(n-e)/i,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,e){return e=e||this._defaultShape(),t=o.latLng(t),e.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new o.LatLngBounds,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return o.Polyline._flat(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var e=[],i=o.Polyline._flat(t),n=0,s=t.length;n<s;n++)i?(e[n]=o.latLng(t[n]),this._bounds.extend(e[n])):e[n]=this._convertLatLngs(t[n]);return e},_project:function(){var t=new o.Bounds;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t);var e=this._clickTolerance(),i=new o.Point(e,e);this._bounds.isValid()&&t.isValid()&&(t.min._subtract(i),t.max._add(i),this._pxBounds=t)},_projectLatlngs:function(t,e,i){var n,s,r=t[0]instanceof o.LatLng,a=t.length;if(r){for(s=[],n=0;n<a;n++)s[n]=this._map.latLngToLayerPoint(t[n]),i.extend(s[n]);e.push(s)}else for(n=0;n<a;n++)this._projectLatlngs(t[n],e,i)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t)){if(this.options.noClip)return void(this._parts=this._rings);var e,i,n,s,r,a,h,l=this._parts;for(e=0,n=0,s=this._rings.length;e<s;e++)for(h=this._rings[e],i=0,r=h.length;i<r-1;i++)a=o.LineUtil.clipSegment(h[i],h[i+1],t,i,!0),a&&(l[n]=l[n]||[],l[n].push(a[0]),a[1]===h[i+1]&&i!==r-2||(l[n].push(a[1]),n++))}},_simplifyPoints:function(){for(var t=this._parts,e=this.options.smoothFactor,i=0,n=t.length;i<n;i++)t[i]=o.LineUtil.simplify(t[i],e)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.Polyline._flat=function(t){return!o.Util.isArray(t[0])||"object"!=typeof t[0][0]&&"undefined"!=typeof t[0][0]},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e,i){var n,s,r,a,h,l,u,c,d,_=[1,4,2,8],m=o.LineUtil;for(s=0,u=t.length;s<u;s++)t[s]._code=m._getBitCode(t[s],e);for(a=0;a<4;a++){for(c=_[a],n=[],s=0,u=t.length,r=u-1;s<u;r=s++)h=t[s],l=t[r],h._code&c?l._code&c||(d=m._getEdgeIntersection(l,h,c,e,i),d._code=m._getBitCode(d,e),n.push(d)):(l._code&c&&(d=m._getEdgeIntersection(l,h,c,e,i),d._code=m._getBitCode(d,e),n.push(d)),n.push(h));t=n}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,e,i,n,o,s,r,a,h,l=this._rings[0],u=l.length;if(!u)return null;for(s=r=a=0,t=0,e=u-1;t<u;e=t++)i=l[t],n=l[e],o=i.y*n.x-n.y*i.x,r+=(i.x+n.x)*o,a+=(i.y+n.y)*o,s+=3*o;return h=0===s?l[0]:[r/s,a/s],this._map.layerPointToLatLng(h)},_convertLatLngs:function(t){var e=o.Polyline.prototype._convertLatLngs.call(this,t),i=e.length;return i>=2&&e[0]instanceof o.LatLng&&e[0].equals(e[i-1])&&e.pop(),e},_setLatLngs:function(t){o.Polyline.prototype._setLatLngs.call(this,t),o.Polyline._flat(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return o.Polyline._flat(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,e=this.options.weight,i=new o.Point(e,e);if(t=new o.Bounds(t.min.subtract(i),t.max.add(i)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t)){if(this.options.noClip)return void(this._parts=this._rings);for(var n,s=0,r=this._rings.length;s<r;s++)n=o.PolyUtil.clipPolygon(this._rings[s],t,!0),n.length&&this._parts.push(n)}},_updatePath:function(){this._renderer._updatePoly(this,!0)}}),o.polygon=function(t,e){return new o.Polygon(t,e)},o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.CircleMarker=o.Path.extend({options:{fill:!0,radius:10},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var e=t&&t.radius||this._radius;return o.Path.prototype.setStyle.call(this,t),this.setRadius(e),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,e=this._radiusY||t,i=this._clickTolerance(),n=[t+i,e+i];this._pxBounds=new o.Bounds(this._point.subtract(n),this._point.add(n))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)}}),o.circleMarker=function(t,e){return new o.CircleMarker(t,e)},o.Circle=o.CircleMarker.extend({initialize:function(t,e,i){if("number"==typeof e&&(e=o.extend({},i,{radius:e})),o.setOptions(this,e),this._latlng=o.latLng(t),isNaN(this.options.radius))throw new Error("Circle radius cannot be NaN");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new o.LatLngBounds(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)));
},setStyle:o.Path.prototype.setStyle,_project:function(){var t=this._latlng.lng,e=this._latlng.lat,i=this._map,n=i.options.crs;if(n.distance===o.CRS.Earth.distance){var s=Math.PI/180,r=this._mRadius/o.CRS.Earth.R/s,a=i.project([e+r,t]),h=i.project([e-r,t]),l=a.add(h).divideBy(2),u=i.unproject(l).lat,c=Math.acos((Math.cos(r*s)-Math.sin(e*s)*Math.sin(u*s))/(Math.cos(e*s)*Math.cos(u*s)))/s;(isNaN(c)||0===c)&&(c=r/Math.cos(Math.PI/180*e)),this._point=l.subtract(i.getPixelOrigin()),this._radius=isNaN(c)?0:Math.max(Math.round(l.x-i.project([u,t-c]).x),1),this._radiusY=Math.max(Math.round(l.y-a.y),1)}else{var d=n.unproject(n.project(this._latlng).subtract([this._mRadius,0]));this._point=i.latLngToLayerPoint(this._latlng),this._radius=this._point.x-i.latLngToLayerPoint(d).x}this._updateBounds()}}),o.circle=function(t,e,i){return new o.Circle(t,e,i)},o.SVG=o.Renderer.extend({getEvents:function(){var t=o.Renderer.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=o.SVG.create("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=o.SVG.create("g"),this._container.appendChild(this._rootGroup)},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){o.Renderer.prototype._update.call(this);var t=this._bounds,e=t.getSize(),i=this._container;this._svgSize&&this._svgSize.equals(e)||(this._svgSize=e,i.setAttribute("width",e.x),i.setAttribute("height",e.y)),o.DomUtil.setPosition(i,t.min),i.setAttribute("viewBox",[t.min.x,t.min.y,e.x,e.y].join(" ")),this.fire("update")}},_initPath:function(t){var e=t._path=o.SVG.create("path");t.options.className&&o.DomUtil.addClass(e,t.options.className),t.options.interactive&&o.DomUtil.addClass(e,"leaflet-interactive"),this._updateStyle(t),this._layers[o.stamp(t)]=t},_addPath:function(t){this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){o.DomUtil.remove(t._path),t.removeInteractiveTarget(t._path),delete this._layers[o.stamp(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var e=t._path,i=t.options;e&&(i.stroke?(e.setAttribute("stroke",i.color),e.setAttribute("stroke-opacity",i.opacity),e.setAttribute("stroke-width",i.weight),e.setAttribute("stroke-linecap",i.lineCap),e.setAttribute("stroke-linejoin",i.lineJoin),i.dashArray?e.setAttribute("stroke-dasharray",i.dashArray):e.removeAttribute("stroke-dasharray"),i.dashOffset?e.setAttribute("stroke-dashoffset",i.dashOffset):e.removeAttribute("stroke-dashoffset")):e.setAttribute("stroke","none"),i.fill?(e.setAttribute("fill",i.fillColor||i.color),e.setAttribute("fill-opacity",i.fillOpacity),e.setAttribute("fill-rule",i.fillRule||"evenodd")):e.setAttribute("fill","none"))},_updatePoly:function(t,e){this._setPath(t,o.SVG.pointsToPath(t._parts,e))},_updateCircle:function(t){var e=t._point,i=t._radius,n=t._radiusY||i,o="a"+i+","+n+" 0 1,0 ",s=t._empty()?"M0 0":"M"+(e.x-i)+","+e.y+o+2*i+",0 "+o+2*-i+",0 ";this._setPath(t,s)},_setPath:function(t,e){t._path.setAttribute("d",e)},_bringToFront:function(t){o.DomUtil.toFront(t._path)},_bringToBack:function(t){o.DomUtil.toBack(t._path)}}),o.extend(o.SVG,{create:function(t){return e.createElementNS("http://www.w3.org/2000/svg",t)},pointsToPath:function(t,e){var i,n,s,r,a,h,l="";for(i=0,s=t.length;i<s;i++){for(a=t[i],n=0,r=a.length;n<r;n++)h=a[n],l+=(n?"L":"M")+h.x+" "+h.y;l+=e?o.Browser.svg?"z":"x":""}return l||"M0 0"}}),o.Browser.svg=!(!e.createElementNS||!o.SVG.create("svg").createSVGRect),o.svg=function(t){return o.Browser.svg||o.Browser.vml?new o.SVG(t):null},o.Browser.vml=!o.Browser.svg&&function(){try{var t=e.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),o.SVG.include(o.Browser.vml?{_initContainer:function(){this._container=o.DomUtil.create("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(o.Renderer.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var e=t._container=o.SVG.create("shape");o.DomUtil.addClass(e,"leaflet-vml-shape "+(this.options.className||"")),e.coordsize="1 1",t._path=o.SVG.create("path"),e.appendChild(t._path),this._updateStyle(t),this._layers[o.stamp(t)]=t},_addPath:function(t){var e=t._container;this._container.appendChild(e),t.options.interactive&&t.addInteractiveTarget(e)},_removePath:function(t){var e=t._container;o.DomUtil.remove(e),t.removeInteractiveTarget(e),delete this._layers[o.stamp(t)]},_updateStyle:function(t){var e=t._stroke,i=t._fill,n=t.options,s=t._container;s.stroked=!!n.stroke,s.filled=!!n.fill,n.stroke?(e||(e=t._stroke=o.SVG.create("stroke")),s.appendChild(e),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=o.Util.isArray(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",e.endcap=n.lineCap.replace("butt","flat"),e.joinstyle=n.lineJoin):e&&(s.removeChild(e),t._stroke=null),n.fill?(i||(i=t._fill=o.SVG.create("fill")),s.appendChild(i),i.color=n.fillColor||n.color,i.opacity=n.fillOpacity):i&&(s.removeChild(i),t._fill=null)},_updateCircle:function(t){var e=t._point.round(),i=Math.round(t._radius),n=Math.round(t._radiusY||i);this._setPath(t,t._empty()?"M0 0":"AL "+e.x+","+e.y+" "+i+","+n+" 0,23592600")},_setPath:function(t,e){t._path.v=e},_bringToFront:function(t){o.DomUtil.toFront(t._container)},_bringToBack:function(t){o.DomUtil.toBack(t._container)}}:{}),o.Browser.vml&&(o.SVG.create=function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}()),o.Canvas=o.Renderer.extend({getEvents:function(){var t=o.Renderer.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){o.Renderer.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=e.createElement("canvas");o.DomEvent.on(t,"mousemove",o.Util.throttle(this._onMouseMove,32,this),this).on(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this).on(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_updatePaths:function(){if(!this._postponeUpdatePaths){var t;this._redrawBounds=null;for(var e in this._layers)t=this._layers[e],t._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},o.Renderer.prototype._update.call(this);var t=this._bounds,e=this._container,i=t.getSize(),n=o.Browser.retina?2:1;o.DomUtil.setPosition(e,t.min),e.width=n*i.x,e.height=n*i.y,e.style.width=i.x+"px",e.style.height=i.y+"px",o.Browser.retina&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){o.Renderer.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[o.stamp(t)]=t;var e=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=e),this._drawLast=e,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var e=t._order,i=e.next,n=e.prev;i?i.prev=n:this._drawLast=n,n?n.next=i:this._drawFirst=i,delete t._order,delete this._layers[o.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var e,i=t.options.dashArray.split(","),n=[];for(e=0;e<i.length;e++)n.push(Number(i[e]));t.options._dashArray=n}},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||o.Util.requestAnimFrame(this._redraw,this))},_extendRedrawBounds:function(t){var e=(t.options.weight||0)+1;this._redrawBounds=this._redrawBounds||new o.Bounds,this._redrawBounds.extend(t._pxBounds.min.subtract([e,e])),this._redrawBounds.extend(t._pxBounds.max.add([e,e]))},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t=this._redrawBounds;if(t){var e=t.getSize();this._ctx.clearRect(t.min.x,t.min.y,e.x,e.y)}else this._ctx.clearRect(0,0,this._container.width,this._container.height)},_draw:function(){var t,e=this._redrawBounds;if(this._ctx.save(),e){var i=e.getSize();this._ctx.beginPath(),this._ctx.rect(e.min.x,e.min.y,i.x,i.y),this._ctx.clip()}this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!e||t._pxBounds&&t._pxBounds.intersects(e))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,e){if(this._drawing){var i,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(this._drawnLayers[t._leaflet_id]=t,h.beginPath(),h.setLineDash&&h.setLineDash(t.options&&t.options._dashArray||[]),i=0;i<a;i++){for(n=0,o=r[i].length;n<o;n++)s=r[i][n],h[n?"lineTo":"moveTo"](s.x,s.y);e&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){if(this._drawing&&!t._empty()){var e=t._point,i=this._ctx,n=t._radius,o=(t._radiusY||n)/n;this._drawnLayers[t._leaflet_id]=t,1!==o&&(i.save(),i.scale(1,o)),i.beginPath(),i.arc(e.x,e.y/o,n,0,2*Math.PI,!1),1!==o&&i.restore(),this._fillStroke(i,t)}},_fillStroke:function(t,e){var i=e.options;i.fill&&(t.globalAlpha=i.fillOpacity,t.fillStyle=i.fillColor||i.color,t.fill(i.fillRule||"evenodd")),i.stroke&&0!==i.weight&&(t.globalAlpha=i.opacity,t.lineWidth=i.weight,t.strokeStyle=i.color,t.lineCap=i.lineCap,t.lineJoin=i.lineJoin,t.stroke())},_onClick:function(t){for(var e,i,n=this._map.mouseEventToLayerPoint(t),s=this._drawFirst;s;s=s.next)e=s.layer,e.options.interactive&&e._containsPoint(n)&&!this._map._draggableMoved(e)&&(i=e);i&&(o.DomEvent._fakeStop(t),this._fireEvent([i],t))},_onMouseMove:function(t){if(this._map&&!this._map.dragging.moving()&&!this._map._animatingZoom){var e=this._map.mouseEventToLayerPoint(t);this._handleMouseHover(t,e)}},_handleMouseOut:function(t){var e=this._hoveredLayer;e&&(o.DomUtil.removeClass(this._container,"leaflet-interactive"),this._fireEvent([e],t,"mouseout"),this._hoveredLayer=null)},_handleMouseHover:function(t,e){for(var i,n,s=this._drawFirst;s;s=s.next)i=s.layer,i.options.interactive&&i._containsPoint(e)&&(n=i);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(o.DomUtil.addClass(this._container,"leaflet-interactive"),this._fireEvent([n],t,"mouseover"),this._hoveredLayer=n)),this._hoveredLayer&&this._fireEvent([this._hoveredLayer],t)},_fireEvent:function(t,e,i){this._map._fireDOMEvent(e,i||e.type,t)},_bringToFront:function(t){var e=t._order,i=e.next,n=e.prev;i&&(i.prev=n,n?n.next=i:i&&(this._drawFirst=i),e.prev=this._drawLast,this._drawLast.next=e,e.next=null,this._drawLast=e,this._requestRedraw(t))},_bringToBack:function(t){var e=t._order,i=e.next,n=e.prev;n&&(n.next=i,i?i.prev=n:n&&(this._drawLast=n),e.prev=null,e.next=this._drawFirst,this._drawFirst.prev=e,this._drawFirst=e,this._requestRedraw(t))}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.canvas=function(t){return o.Browser.canvas?new o.Canvas(t):null},o.Polyline.prototype._containsPoint=function(t,e){var i,n,s,r,a,h,l=this._clickTolerance();if(!this._pxBounds.contains(t))return!1;for(i=0,r=this._parts.length;i<r;i++)for(h=this._parts[i],n=0,a=h.length,s=a-1;n<a;s=n++)if((e||0!==n)&&o.LineUtil.pointToSegmentDistance(t,h[s],h[n])<=l)return!0;return!1},o.Polygon.prototype._containsPoint=function(t){var e,i,n,s,r,a,h,l,u=!1;if(!this._pxBounds.contains(t))return!1;for(s=0,h=this._parts.length;s<h;s++)for(e=this._parts[s],r=0,l=e.length,a=l-1;r<l;a=r++)i=e[r],n=e[a],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u||o.Polyline.prototype._containsPoint.call(this,t,!0)},o.CircleMarker.prototype._containsPoint=function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()},o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;e<i;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var r=this.options;if(r.filter&&!r.filter(t))return this;var a=o.GeoJSON.geometryToLayer(t,r);return a?(a.feature=o.GeoJSON.asFeature(t),a.defaultOptions=a.options,this.resetStyle(a),r.onEachFeature&&r.onEachFeature(t,a),this.addLayer(a)):this},resetStyle:function(t){return t.options=o.Util.extend({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this},setStyle:function(t){return this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e){var i,n,s,r,a="Feature"===t.type?t.geometry:t,h=a?a.coordinates:null,l=[],u=e&&e.pointToLayer,c=e&&e.coordsToLatLng||this.coordsToLatLng;if(!h&&!a)return null;switch(a.type){case"Point":return i=c(h),u?u(t,i):new o.Marker(i);case"MultiPoint":for(s=0,r=h.length;s<r;s++)i=c(h[s]),l.push(u?u(t,i):new o.Marker(i));return new o.FeatureGroup(l);case"LineString":case"MultiLineString":return n=this.coordsToLatLngs(h,"LineString"===a.type?0:1,c),new o.Polyline(n,e);case"Polygon":case"MultiPolygon":return n=this.coordsToLatLngs(h,"Polygon"===a.type?1:2,c),new o.Polygon(n,e);case"GeometryCollection":for(s=0,r=a.geometries.length;s<r;s++){var d=this.geometryToLayer({geometry:a.geometries[s],type:"Feature",properties:t.properties},e);d&&l.push(d)}return new o.FeatureGroup(l);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0],t[2])},coordsToLatLngs:function(t,e,i){for(var n,o=[],s=0,r=t.length;s<r;s++)n=e?this.coordsToLatLngs(t[s],e-1,i):(i||this.coordsToLatLng)(t[s]),o.push(n);return o},latLngToCoords:function(t){return t.alt!==i?[t.lng,t.lat,t.alt]:[t.lng,t.lat]},latLngsToCoords:function(t,e,i){for(var n=[],s=0,r=t.length;s<r;s++)n.push(e?o.GeoJSON.latLngsToCoords(t[s],e-1,i):o.GeoJSON.latLngToCoords(t[s]));return!e&&i&&n.push(n[0]),n},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.prototype.toGeoJSON=function(){var t=!o.Polyline._flat(this._latlngs),e=o.GeoJSON.latLngsToCoords(this._latlngs,t?1:0);return o.GeoJSON.getFeature(this,{type:(t?"Multi":"")+"LineString",coordinates:e})},o.Polygon.prototype.toGeoJSON=function(){var t=!o.Polyline._flat(this._latlngs),e=t&&!o.Polyline._flat(this._latlngs[0]),i=o.GeoJSON.latLngsToCoords(this._latlngs,e?2:t?1:0,!0);return t||(i=[i]),o.GeoJSON.getFeature(this,{type:(e?"Multi":"")+"Polygon",coordinates:i})},o.LayerGroup.include({toMultiPoint:function(){var t=[];return this.eachLayer(function(e){t.push(e.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:"MultiPoint",coordinates:t})},toGeoJSON:function(){var t=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===t)return this.toMultiPoint();var e="GeometryCollection"===t,i=[];return this.eachLayer(function(t){if(t.toGeoJSON){var n=t.toGeoJSON();i.push(e?n.geometry:o.GeoJSON.asFeature(n))}}),e?o.GeoJSON.getFeature(this,{geometries:i,type:"GeometryCollection"}):{type:"FeatureCollection",features:i}}}),o.geoJSON=function(t,e){return new o.GeoJSON(t,e)},o.geoJson=o.geoJSON,o.Draggable=o.Evented.extend({options:{clickTolerance:3},statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e,i){this._element=t,this._dragStartTarget=e||t,this._preventOutline=i},enable:function(){this._enabled||(o.DomEvent.on(this._dragStartTarget,o.Draggable.START.join(" "),this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(o.Draggable._dragging===this&&this.finishDrag(),o.DomEvent.off(this._dragStartTarget,o.Draggable.START.join(" "),this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!o.DomUtil.hasClass(this._element,"leaflet-zoom-anim")&&!(o.Draggable._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(o.Draggable._dragging=this,this._preventOutline&&o.DomUtil.preventOutline(this._element),o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new o.Point(i.clientX,i.clientY),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this)}},_onMove:function(i){if(!i._simulated&&this._enabled){if(i.touches&&i.touches.length>1)return void(this._moved=!0);var n=i.touches&&1===i.touches.length?i.touches[0]:i,s=new o.Point(n.clientX,n.clientY),r=s.subtract(this._startPoint);(r.x||r.y)&&(Math.abs(r.x)+Math.abs(r.y)<this.options.clickTolerance||(o.DomEvent.preventDefault(i),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(r),o.DomUtil.addClass(e.body,"leaflet-dragging"),this._lastTarget=i.target||i.srcElement,t.SVGElementInstance&&this._lastTarget instanceof SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),o.DomUtil.addClass(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(r),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._lastEvent=i,this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0)))}},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire("predrag",t),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag",t)},_onUp:function(t){!t._simulated&&this._enabled&&this.finishDrag()},finishDrag:function(){o.DomUtil.removeClass(e.body,"leaflet-dragging"),this._lastTarget&&(o.DomUtil.removeClass(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove,this).off(e,o.Draggable.END[t],this._onUp,this);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&this._moving&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1,o.Draggable._dragging=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({down:this._onDown,dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}o.DomUtil.addClass(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){o.DomUtil.removeClass(this._map._container,"leaflet-grab"),o.DomUtil.removeClass(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDown:function(){this._map._stop()},_onDragStart:function(){var t=this._map;if(this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var e=o.latLngBounds(this._map.options.maxBounds);this._offsetLimit=o.bounds(this._map.latLngToContainerPoint(e.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(e.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var e=this._lastTime=+new Date,i=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(i),this._times.push(e),e-this._times[0]>50&&(this._positions.shift(),this._times.shift())}this._map.fire("move",t).fire("drag",t)},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,e){return t-(t-e)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),e=this._offsetLimit;t.x<e.min.x&&(t.x=this._viscousLimit(t.x,e.min.x)),t.y<e.min.y&&(t.y=this._viscousLimit(t.y,e.min.y)),t.x>e.max.x&&(t.x=this._viscousLimit(t.x,e.max.x)),t.y>e.max.y&&(t.y=this._viscousLimit(t.y,e.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,r=Math.abs(o+i)<Math.abs(s+i)?o:s;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=r},_onDragEnd:function(t){var e=this._map,i=e.options,n=!i.inertia||this._times.length<2;if(e.fire("dragend",t),n)e.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),r=(this._lastTime-this._times[0])/1e3,a=i.easeLinearity,h=s.multiplyBy(a/r),l=h.distanceTo([0,0]),u=Math.min(i.inertiaMaxSpeed,l),c=h.multiplyBy(u/l),d=u/(i.inertiaDeceleration*a),_=c.multiplyBy(-d/2).round();_.x||_.y?(_=e._limitOffset(_,e.options.maxBounds),o.Util.requestAnimFrame(function(){e.panBy(_,{duration:d,easeLinearity:a,noMoveStart:!0,animate:!0})})):e.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom(),n=e.options.zoomDelta,o=t.originalEvent.shiftKey?i-n:i+n;"center"===e.options.doubleClickZoom?e.setZoom(o):e.setZoomAround(t.containerPoint,o)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll,this)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t),i=this._map.options.wheelDebounceTime;this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var n=Math.max(i-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),n),o.DomEvent.stop(t)},_performZoom:function(){var t=this._map,e=t.getZoom(),i=this._map.options.zoomSnap||0;t._stop();var n=this._delta/(4*this._map.options.wheelPxPerZoomLevel),o=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,s=i?Math.ceil(o/i)*i:o,r=t._limitZoom(e+(this._delta>0?s:-s))-e;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(e+r):t.setZoomAround(this._lastMousePos,e+r))}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msPointer?"MSPointerDown":o.Browser.pointer?"pointerdown":"touchstart",_touchend:o.Browser.msPointer?"MSPointerUp":o.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(t,e,i){function n(t){var e;if(o.Browser.pointer){if(!o.Browser.edge||"mouse"===t.pointerType)return;e=o.DomEvent._pointersCount}else e=t.touches.length;if(!(e>1)){var i=Date.now(),n=i-(r||i);a=t.touches?t.touches[0]:t,h=n>0&&n<=l,r=i}}function s(t){if(h&&!a.cancelBubble){if(o.Browser.pointer){if(!o.Browser.edge||"mouse"===t.pointerType)return;var i,n,s={};for(n in a)i=a[n],s[n]=i&&i.bind?i.bind(a):i;a=s}a.type="dblclick",e(a),r=null}}var r,a,h=!1,l=250,u="_leaflet_",c=this._touchstart,d=this._touchend;return t[u+c+i]=n,t[u+d+i]=s,t[u+"dblclick"+i]=e,t.addEventListener(c,n,!1),t.addEventListener(d,s,!1),t.addEventListener("dblclick",e,!1),this},removeDoubleTapListener:function(t,e){var i="_leaflet_",n=t[i+this._touchstart+e],s=t[i+this._touchend+e],r=t[i+"dblclick"+e];return t.removeEventListener(this._touchstart,n,!1),t.removeEventListener(this._touchend,s,!1),o.Browser.edge||t.removeEventListener("dblclick",r,!1),this}}),o.extend(o.DomEvent,{POINTER_DOWN:o.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:o.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:o.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:o.Browser.msPointer?"MSPointerCancel":"pointercancel",TAG_WHITE_LIST:["INPUT","SELECT","OPTION"],_pointers:{},_pointersCount:0,addPointerListener:function(t,e,i,n){return"touchstart"===e?this._addPointerStart(t,i,n):"touchmove"===e?this._addPointerMove(t,i,n):"touchend"===e&&this._addPointerEnd(t,i,n),this},removePointerListener:function(t,e,i){var n=t["_leaflet_"+e+i];return"touchstart"===e?t.removeEventListener(this.POINTER_DOWN,n,!1):"touchmove"===e?t.removeEventListener(this.POINTER_MOVE,n,!1):"touchend"===e&&(t.removeEventListener(this.POINTER_UP,n,!1),t.removeEventListener(this.POINTER_CANCEL,n,!1)),this},_addPointerStart:function(t,i,n){var s=o.bind(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(this.TAG_WHITE_LIST.indexOf(t.target.tagName)<0))return;o.DomEvent.preventDefault(t)}this._handlePointer(t,i)},this);if(t["_leaflet_touchstart"+n]=s,t.addEventListener(this.POINTER_DOWN,s,!1),!this._pointerDocListener){var r=o.bind(this._globalPointerUp,this);e.documentElement.addEventListener(this.POINTER_DOWN,o.bind(this._globalPointerDown,this),!0),e.documentElement.addEventListener(this.POINTER_MOVE,o.bind(this._globalPointerMove,this),!0),e.documentElement.addEventListener(this.POINTER_UP,r,!0),e.documentElement.addEventListener(this.POINTER_CANCEL,r,!0),this._pointerDocListener=!0}},_globalPointerDown:function(t){this._pointers[t.pointerId]=t,this._pointersCount++},_globalPointerMove:function(t){this._pointers[t.pointerId]&&(this._pointers[t.pointerId]=t)},_globalPointerUp:function(t){delete this._pointers[t.pointerId],this._pointersCount--},_handlePointer:function(t,e){t.touches=[];for(var i in this._pointers)t.touches.push(this._pointers[i]);t.changedTouches=[t],e(t)},_addPointerMove:function(t,e,i){var n=o.bind(function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&this._handlePointer(t,e)},this);t["_leaflet_touchmove"+i]=n,t.addEventListener(this.POINTER_MOVE,n,!1)},_addPointerEnd:function(t,e,i){var n=o.bind(function(t){this._handlePointer(t,e)},this);t["_leaflet_touchend"+i]=n,t.addEventListener(this.POINTER_UP,n,!1),t.addEventListener(this.POINTER_CANCEL,n,!1)}}),o.Map.mergeOptions({touchZoom:o.Browser.touch&&!o.Browser.android23,bounceAtZoomLimits:!0}),o.Map.TouchZoom=o.Handler.extend({addHooks:function(){o.DomUtil.addClass(this._map._container,"leaflet-touch-zoom"),o.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){o.DomUtil.removeClass(this._map._container,"leaflet-touch-zoom"),o.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var n=i.mouseEventToContainerPoint(t.touches[0]),s=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(n.add(s)._divideBy(2))),this._startDist=n.distanceTo(s),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),o.DomEvent.on(e,"touchmove",this._onTouchMove,this).on(e,"touchend",this._onTouchEnd,this),o.DomEvent.preventDefault(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var e=this._map,i=e.mouseEventToContainerPoint(t.touches[0]),n=e.mouseEventToContainerPoint(t.touches[1]),s=i.distanceTo(n)/this._startDist;if(this._zoom=e.getScaleZoom(s,this._startZoom),!e.options.bounceAtZoomLimits&&(this._zoom<e.getMinZoom()&&s<1||this._zoom>e.getMaxZoom()&&s>1)&&(this._zoom=e._limitZoom(this._zoom)),"center"===e.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=i._add(n)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=e.unproject(e.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(e._moveStart(!0),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest);var a=o.bind(e._move,e,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=o.Util.requestAnimFrame(a,this,!0),o.DomEvent.preventDefault(t)}},_onTouchEnd:function(){return this._moved&&this._zooming?(this._zooming=!1,o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd),void(this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom)))):void(this._zooming=!1)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),o.DomEvent.on(e,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY),
this._simulateEvent("mousemove",e)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_resetState:function(){this._moved=!1},_onMouseDown:function(t){return!(!t.shiftKey||1!==t.which&&1!==t.button)&&(this._resetState(),o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startPoint=this._map.mouseEventToContainerPoint(t),void o.DomEvent.on(e,{contextmenu:o.DomEvent.stop,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this))},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=o.DomUtil.create("div","leaflet-zoom-box",this._container),o.DomUtil.addClass(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var e=new o.Bounds(this._point,this._startPoint),i=e.getSize();o.DomUtil.setPosition(this._box,e.min),this._box.style.width=i.x+"px",this._box.style.height=i.y+"px"},_finish:function(){this._moved&&(o.DomUtil.remove(this._box),o.DomUtil.removeClass(this._container,"leaflet-crosshair")),o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,{contextmenu:o.DomEvent.stop,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){setTimeout(o.bind(this._resetState,this),0);var e=new o.LatLngBounds(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(e).fire("boxzoomend",{boxZoomBounds:e})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanDelta:80}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex="0"),o.DomEvent.on(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),o.DomEvent.off(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanDelta:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;e<i;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;e<i;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;e<i;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;e<i;e++)n[o.up[e]]=[0,-1*t]},_setZoomDelta:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;e<i;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;e<i;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var e,i=t.keyCode,n=this._map;if(i in this._panKeys){if(n._panAnim&&n._panAnim._inProgress)return;e=this._panKeys[i],t.shiftKey&&(e=o.point(e).multiplyBy(3)),n.panBy(e),n.options.maxBounds&&n.panInsideBounds(n.options.maxBounds)}else if(i in this._zoomKeys)n.setZoom(n.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[i]);else{if(27!==i)return;n.closePopup()}o.DomEvent.stop(t)}}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),o.DomUtil.addClass(t,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(t){var e=this._marker,i=e._shadow,n=o.DomUtil.getPosition(e._icon),s=e._map.layerPointToLatLng(n);i&&o.DomUtil.setPosition(i,n),e._latlng=s,t.latlng=s,t.oldLatLng=this._oldLatLng,e.fire("move",t).fire("drag",t)},_onDragEnd:function(t){delete this._oldLatLng,this._marker.fire("moveend").fire("dragend",t)}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),i.indexOf("bottom")!==-1?n.insertBefore(e,n.firstChild):n.appendChild(e),this},remove:function(){return this._map?(o.DomUtil.remove(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,s){var r=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",r,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){o.DomUtil.remove(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,e+"-in",i,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,e+"-out",i,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,e,i,n,s){var r=o.DomUtil.create("a",i,n);return r.innerHTML=t,r.href="#",r.title=e,r.setAttribute("role","button"),r.setAttribute("aria-label",e),o.DomEvent.on(r,"mousedown dblclick",o.DomEvent.stopPropagation).on(r,"click",o.DomEvent.stop).on(r,"click",s,this).on(r,"click",this._refocusOnMap,this),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),(this._disabled||t._zoom===t.getMinZoom())&&o.DomUtil.addClass(this._zoomOutButton,e),(this._disabled||t._zoom===t.getMaxZoom())&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent&&o.DomEvent.disableClickPropagation(this._container);for(var e in t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(new o.Control.Attribution).addTo(this)}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e+"-line",i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e,i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e,i))},_update:function(){var t=this._map,e=t.getSize().y/2,i=t.distance(t.containerPointToLatLng([0,e]),t.containerPointToLatLng([this.options.maxWidth,e]));this._updateScales(i)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var e=this._getRoundNum(t),i=e<1e3?e+" m":e/1e3+" km";this._updateScale(this._mScale,i,e/t)},_updateImperial:function(t){var e,i,n,o=3.2808399*t;o>5280?(e=o/5280,i=this._getRoundNum(e),this._updateScale(this._iScale,i+" mi",i/e)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,e,i){t.style.width=Math.round(this.options.maxWidth*i)+"px",t.innerHTML=e},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,e,i,n){return i<n?-1:n<i?1:0}},initialize:function(t,e,i){o.setOptions(this,i),this._layers=[],this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),this._map=t,t.on("zoomend",this._checkDisabledLayers,this),this._container},onRemove:function(){this._map.off("zoomend",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off("add remove",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._map?this._update():this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._map?this._update():this},removeLayer:function(t){t.off("add remove",this._onLayerChange,this);var e=this._getLayer(o.stamp(t));return e&&this._layers.splice(this._layers.indexOf(e),1),this._map?this._update():this},expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded"),this._form.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._form.clientHeight?(o.DomUtil.addClass(this._form,"leaflet-control-layers-scrollbar"),this._form.style.height=t+"px"):o.DomUtil.removeClass(this._form,"leaflet-control-layers-scrollbar"),this._checkDisabledLayers(),this},collapse:function(){return o.DomUtil.removeClass(this._container,"leaflet-control-layers-expanded"),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t),i=this.options.collapsed;e.setAttribute("aria-haspopup",!0),o.DomEvent.disableClickPropagation(e),o.Browser.touch||o.DomEvent.disableScrollPropagation(e);var n=this._form=o.DomUtil.create("form",t+"-list");i&&(this._map.on("click",this.collapse,this),o.Browser.android||o.DomEvent.on(e,{mouseenter:this.expand,mouseleave:this.collapse},this));var s=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);s.href="#",s.title="Layers",o.Browser.touch?o.DomEvent.on(s,"click",o.DomEvent.stop).on(s,"click",this.expand,this):o.DomEvent.on(s,"focus",this.expand,this),o.DomEvent.on(n,"click",function(){setTimeout(o.bind(this._onInputClick,this),0)},this),i||this.expand(),this._baseLayersList=o.DomUtil.create("div",t+"-base",n),this._separator=o.DomUtil.create("div",t+"-separator",n),this._overlaysList=o.DomUtil.create("div",t+"-overlays",n),e.appendChild(n)},_getLayer:function(t){for(var e=0;e<this._layers.length;e++)if(this._layers[e]&&o.stamp(this._layers[e].layer)===t)return this._layers[e]},_addLayer:function(t,e,i){t.on("add remove",this._onLayerChange,this),this._layers.push({layer:t,name:e,overlay:i}),this.options.sortLayers&&this._layers.sort(o.bind(function(t,e){return this.options.sortFunction(t.layer,e.layer,t.name,e.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(!this._container)return this;o.DomUtil.empty(this._baseLayersList),o.DomUtil.empty(this._overlaysList);var t,e,i,n,s=0;for(i=0;i<this._layers.length;i++)n=this._layers[i],this._addItem(n),e=e||n.overlay,t=t||!n.overlay,s+=n.overlay?0:1;return this.options.hideSingleBase&&(t=t&&s>1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=e&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var e=this._getLayer(o.stamp(t.target)),i=e.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)},_createRadioElement:function(t,i){var n='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"'+(i?' checked="checked"':"")+"/>",o=e.createElement("div");return o.innerHTML=n,o.firstChild},_addItem:function(t){var i,n=e.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?(i=e.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers",s),i.layerId=o.stamp(t.layer),o.DomEvent.on(i,"click",this._onInputClick,this);var r=e.createElement("span");r.innerHTML=" "+t.name;var a=e.createElement("div");n.appendChild(a),a.appendChild(i),a.appendChild(r);var h=t.overlay?this._overlaysList:this._baseLayersList;return h.appendChild(n),this._checkDisabledLayers(),n},_onInputClick:function(){var t,e,i,n=this._form.getElementsByTagName("input"),o=[],s=[];this._handlingClick=!0;for(var r=n.length-1;r>=0;r--)t=n[r],e=this._getLayer(t.layerId).layer,i=this._map.hasLayer(e),t.checked&&!i?o.push(e):!t.checked&&i&&s.push(e);for(r=0;r<s.length;r++)this._map.removeLayer(s[r]);for(r=0;r<o.length;r++)this._map.addLayer(o[r]);this._handlingClick=!1,this._refocusOnMap()},_checkDisabledLayers:function(){for(var t,e,n=this._form.getElementsByTagName("input"),o=this._map.getZoom(),s=n.length-1;s>=0;s--)t=n[s],e=this._getLayer(t.layerId).layer,t.disabled=e.options.minZoom!==i&&o<e.options.minZoom||e.options.maxZoom!==i&&o>e.options.maxZoom},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)}}(window,document);
/* esri-leaflet - v2.3.1 - Thu Oct 10 2019 14:43:44 GMT-0500 (Central Daylight Time)
 * Copyright (c) 2019 Environmental Systems Research Institute, Inc.
 * Apache-2.0 */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("leaflet")):"function"==typeof define&&define.amd?define('esri-leaflet',["exports","leaflet"],e):e((t.L=t.L||{},t.L.esri={}),t.L)}(this,function(t,e){"use strict";var i=window.XMLHttpRequest&&"withCredentials"in new window.XMLHttpRequest,s=""===document.documentElement.style.pointerEvents,r={cors:i,pointerEvents:s},n={attributionWidthOffset:55},o=0;function a(t){var e="";for(var i in t.f=t.f||"json",t)if(t.hasOwnProperty(i)){var s,r=t[i],n=Object.prototype.toString.call(r);e.length&&(e+="&"),s="[object Array]"===n?"[object Object]"===Object.prototype.toString.call(r[0])?JSON.stringify(r):r.join(","):"[object Object]"===n?JSON.stringify(r):"[object Date]"===n?r.valueOf():r,e+=encodeURIComponent(i)+"="+encodeURIComponent(s)}return e}function u(t,i){var s=new window.XMLHttpRequest;return s.onerror=function(r){s.onreadystatechange=e.Util.falseFn,t.call(i,{error:{code:500,message:"XMLHttpRequest error"}},null)},s.onreadystatechange=function(){var r,n;if(4===s.readyState){try{r=JSON.parse(s.responseText)}catch(t){r=null,n={code:500,message:"Could not parse response as JSON. This could also be caused by a CORS or XMLHttpRequest error."}}!n&&r.error&&(n=r.error,r=null),s.onerror=e.Util.falseFn,t.call(i,n,r)}},s.ontimeout=function(){this.onerror()},s}function l(t,e,i,s){var r=u(i,s);return r.open("POST",t),void 0!==s&&null!==s&&void 0!==s.options&&(r.timeout=s.options.timeout),r.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),r.send(a(e)),r}function h(t,e,i,s){var r=u(i,s);return r.open("GET",t+"?"+a(e),!0),void 0!==s&&null!==s&&void 0!==s.options&&(r.timeout=s.options.timeout),r.send(null),r}function p(t,e,i,s){var n=a(e),o=u(i,s),l=(t+"?"+n).length;if(l<=2e3&&r.cors?o.open("GET",t+"?"+n):l>2e3&&r.cors&&(o.open("POST",t),o.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8")),void 0!==s&&null!==s&&void 0!==s.options&&(o.timeout=s.options.timeout),l<=2e3&&r.cors)o.send(null);else{if(!(l>2e3&&r.cors))return l<=2e3&&!r.cors?c(t,e,i,s):void q("a request to "+t+" was longer then 2000 characters and this browser cannot make a cross-domain post request. Please use a proxy http://esri.github.io/esri-leaflet/api-reference/request.html");o.send(n)}return o}function c(t,i,s,r){window._EsriLeafletCallbacks=window._EsriLeafletCallbacks||{};var n="c"+o;i.callback="window._EsriLeafletCallbacks."+n,window._EsriLeafletCallbacks[n]=function(t){if(!0!==window._EsriLeafletCallbacks[n]){var e,i=Object.prototype.toString.call(t);"[object Object]"!==i&&"[object Array]"!==i&&(e={error:{code:500,message:"Expected array or object as JSONP response"}},t=null),!e&&t.error&&(e=t,t=null),s.call(r,e,t),window._EsriLeafletCallbacks[n]=!0}};var u=e.DomUtil.create("script",null,document.body);return u.type="text/javascript",u.src=t+"?"+a(i),u.id=n,u.onerror=function(t){if(t&&!0!==window._EsriLeafletCallbacks[n]){s.call(r,{error:{code:500,message:"An unknown error occurred"}}),window._EsriLeafletCallbacks[n]=!0}},e.DomUtil.addClass(u,"esri-leaflet-jsonp"),o++,{id:n,url:u.src,abort:function(){window._EsriLeafletCallbacks._callback[n]({code:0,message:"Request aborted."})}}}var m=r.cors?h:c;m.CORS=h,m.JSONP=c;var d={request:p,get:m,post:l};function f(t){return function(t,e){for(var i=0;i<t.length;i++)if(t[i]!==e[i])return!1;return!0}(t[0],t[t.length-1])||t.push(t[0]),t}function y(t){for(var e,i=0,s=0,r=t.length,n=t[s];s<r-1;s++)i+=((e=t[s+1])[0]-n[0])*(e[1]+n[1]),n=e;return i>=0}function g(t,e,i,s){var r=(s[0]-i[0])*(t[1]-i[1])-(s[1]-i[1])*(t[0]-i[0]),n=(e[0]-t[0])*(t[1]-i[1])-(e[1]-t[1])*(t[0]-i[0]),o=(s[1]-i[1])*(e[0]-t[0])-(s[0]-i[0])*(e[1]-t[1]);if(0!==o){var a=r/o,u=n/o;if(a>=0&&a<=1&&u>=0&&u<=1)return!0}return!1}function v(t,e){for(var i=0;i<t.length-1;i++)for(var s=0;s<e.length-1;s++)if(g(t[i],t[i+1],e[s],e[s+1]))return!0;return!1}function _(t,e){var i=v(t,e),s=function(t,e){for(var i=!1,s=-1,r=t.length,n=r-1;++s<r;n=s)(t[s][1]<=e[1]&&e[1]<t[n][1]||t[n][1]<=e[1]&&e[1]<t[s][1])&&e[0]<(t[n][0]-t[s][0])*(e[1]-t[s][1])/(t[n][1]-t[s][1])+t[s][0]&&(i=!i);return i}(t,e[0]);return!(i||!s)}function b(t){var e=[],i=t.slice(0),s=f(i.shift().slice(0));if(s.length>=4){y(s)||s.reverse(),e.push(s);for(var r=0;r<i.length;r++){var n=f(i[r].slice(0));n.length>=4&&(y(n)&&n.reverse(),e.push(n))}}return e}function x(t){var e={};for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e}function S(t,e){var i={};if(t.features){i.type="FeatureCollection",i.features=[];for(var s=0;s<t.features.length;s++)i.features.push(S(t.features[s],e))}if("number"==typeof t.x&&"number"==typeof t.y&&(i.type="Point",i.coordinates=[t.x,t.y],"number"==typeof t.z&&i.coordinates.push(t.z)),t.points&&(i.type="MultiPoint",i.coordinates=t.points.slice(0)),t.paths&&(1===t.paths.length?(i.type="LineString",i.coordinates=t.paths[0].slice(0)):(i.type="MultiLineString",i.coordinates=t.paths.slice(0))),t.rings&&(i=function(t){for(var e,i,s=[],r=[],n=0;n<t.length;n++){var o=f(t[n].slice(0));if(!(o.length<4))if(y(o)){var a=[o.slice().reverse()];s.push(a)}else r.push(o.slice().reverse())}for(var u=[];r.length;){i=r.pop();var l=!1;for(e=s.length-1;e>=0;e--)if(_(s[e][0],i)){s[e].push(i),l=!0;break}l||u.push(i)}for(;u.length;){i=u.pop();var h=!1;for(e=s.length-1;e>=0;e--)if(v(s[e][0],i)){s[e].push(i),h=!0;break}h||s.push([i.reverse()])}return 1===s.length?{type:"Polygon",coordinates:s[0]}:{type:"MultiPolygon",coordinates:s}}(t.rings.slice(0))),"number"==typeof t.xmin&&"number"==typeof t.ymin&&"number"==typeof t.xmax&&"number"==typeof t.ymax&&(i.type="Polygon",i.coordinates=[[[t.xmax,t.ymax],[t.xmin,t.ymax],[t.xmin,t.ymin],[t.xmax,t.ymin],[t.xmax,t.ymax]]]),(t.geometry||t.attributes)&&(i.type="Feature",i.geometry=t.geometry?S(t.geometry):null,i.properties=t.attributes?x(t.attributes):null,t.attributes))try{i.id=function(t,e){for(var i=e?[e,"OBJECTID","FID"]:["OBJECTID","FID"],s=0;s<i.length;s++){var r=i[s];if(r in t&&("string"==typeof t[r]||"number"==typeof t[r]))return t[r]}throw Error("No valid id attribute found")}(t.attributes,e)}catch(t){}return JSON.stringify(i.geometry)===JSON.stringify({})&&(i.geometry=null),t.spatialReference&&t.spatialReference.wkid&&4326!==t.spatialReference.wkid&&console.warn("Object converted in non-standard crs - "+JSON.stringify(t.spatialReference)),i}function I(t,e){e=e||"OBJECTID";var i,s={wkid:4326},r={};switch(t.type){case"Point":r.x=t.coordinates[0],r.y=t.coordinates[1],r.spatialReference=s;break;case"MultiPoint":r.points=t.coordinates.slice(0),r.spatialReference=s;break;case"LineString":r.paths=[t.coordinates.slice(0)],r.spatialReference=s;break;case"MultiLineString":r.paths=t.coordinates.slice(0),r.spatialReference=s;break;case"Polygon":r.rings=b(t.coordinates.slice(0)),r.spatialReference=s;break;case"MultiPolygon":r.rings=function(t){for(var e=[],i=0;i<t.length;i++)for(var s=b(t[i]),r=s.length-1;r>=0;r--){var n=s[r].slice(0);e.push(n)}return e}(t.coordinates.slice(0)),r.spatialReference=s;break;case"Feature":t.geometry&&(r.geometry=I(t.geometry,e)),r.attributes=t.properties?x(t.properties):{},t.id&&(r.attributes[e]=t.id);break;case"FeatureCollection":for(r=[],i=0;i<t.features.length;i++)r.push(I(t.features[i],e));break;case"GeometryCollection":for(r=[],i=0;i<t.geometries.length;i++)r.push(I(t.geometries[i],e))}return r}function A(t,e){return I(t,e)}function T(t,e){return S(t,e)}function w(t){if("NaN"!==t.xmin&&"NaN"!==t.ymin&&"NaN"!==t.xmax&&"NaN"!==t.ymax){var i=e.latLng(t.ymin,t.xmin),s=e.latLng(t.ymax,t.xmax);return e.latLngBounds(i,s)}return null}function R(t){return{xmin:(t=e.latLngBounds(t)).getSouthWest().lng,ymin:t.getSouthWest().lat,xmax:t.getNorthEast().lng,ymax:t.getNorthEast().lat,spatialReference:{wkid:4326}}}var O=/^(OBJECTID|FID|OID|ID)$/i;function P(t){var e;if(t.objectIdFieldName)e=t.objectIdFieldName;else if(t.fields){for(var i=0;i<=t.fields.length-1;i++)if("esriFieldTypeOID"===t.fields[i].type){e=t.fields[i].name;break}if(!e)for(i=0;i<=t.fields.length-1;i++)if(t.fields[i].name.match(O)){e=t.fields[i].name;break}}return e}function C(t){for(var e in t.attributes)if(e.match(O))return e}function F(t,e){var i,s=t.features||t.results,r=s.length;i=e||P(t);var n={type:"FeatureCollection",features:[]};if(r)for(var o=s.length-1;o>=0;o--){var a=T(s[o],i||C(s[o]));n.features.push(a)}return n}function U(t){return"/"!==(t=e.Util.trim(t))[t.length-1]&&(t+="/"),t}function k(t){if(-1!==t.url.indexOf("?")){t.requestParams=t.requestParams||{};var e=t.url.substring(t.url.indexOf("?")+1);t.url=t.url.split("?")[0],t.requestParams=JSON.parse('{"'+decodeURI(e).replace(/"/g,'\\"').replace(/&/g,'","').replace(/=/g,'":"')+'"}')}return t.url=U(t.url.split("?")[0]),t}function G(t){return/^(?!.*utility\.arcgis\.com).*\.arcgis\.com.*FeatureServer/i.test(t)}function M(t){var e;switch(t){case"Point":e="esriGeometryPoint";break;case"MultiPoint":e="esriGeometryMultipoint";break;case"LineString":case"MultiLineString":e="esriGeometryPolyline";break;case"Polygon":case"MultiPolygon":e="esriGeometryPolygon"}return e}function q(){console&&console.warn&&console.warn.apply(console,arguments)}function D(t){return t.getSize().x-n.attributionWidthOffset+"px"}function E(t){if(t.attributionControl&&!t.attributionControl._esriAttributionAdded){t.attributionControl.setPrefix('<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a> | Powered by Esri');var i=document.createElement("style");i.type="text/css",i.innerHTML=".esri-truncated-attribution:hover {white-space: normal;}",document.getElementsByTagName("head")[0].appendChild(i),e.DomUtil.addClass(t.attributionControl._container,"esri-truncated-attribution:hover");var s=document.createElement("style");s.type="text/css",s.innerHTML=".esri-truncated-attribution {vertical-align: -3px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;display: inline-block;transition: 0s white-space;transition-delay: 1s;max-width: "+D(t)+";}",document.getElementsByTagName("head")[0].appendChild(s),e.DomUtil.addClass(t.attributionControl._container,"esri-truncated-attribution"),t.on("resize",function(e){t.attributionControl._container.style.maxWidth=D(e.target)}),t.attributionControl._esriAttributionAdded=!0}}function z(t){var i={geometry:null,geometryType:null};return t instanceof e.LatLngBounds?(i.geometry=R(t),i.geometryType="esriGeometryEnvelope",i):(t.getLatLng&&(t=t.getLatLng()),t instanceof e.LatLng&&(t={type:"Point",coordinates:[t.lng,t.lat]}),t instanceof e.GeoJSON&&(t=t.getLayers()[0].feature.geometry,i.geometry=A(t),i.geometryType=M(t.type)),t.toGeoJSON&&(t=t.toGeoJSON()),"Feature"===t.type&&(t=t.geometry),"Point"===t.type||"LineString"===t.type||"Polygon"===t.type||"MultiPolygon"===t.type?(i.geometry=A(t),i.geometryType=M(t.type),i):void q("invalid geometry passed to spatial query. Should be L.LatLng, L.LatLngBounds, L.Marker or a GeoJSON Point, Line, Polygon or MultiPolygon object"))}function B(t,i){r.cors&&p(t,{},e.Util.bind(function(t,s){if(!t){i._esriAttributions=[];for(var r=0;r<s.contributors.length;r++)for(var n=s.contributors[r],o=0;o<n.coverageAreas.length;o++){var a=n.coverageAreas[o],u=e.latLng(a.bbox[0],a.bbox[1]),l=e.latLng(a.bbox[2],a.bbox[3]);i._esriAttributions.push({attribution:n.attribution,score:a.score,bounds:e.latLngBounds(u,l),minZoom:a.zoomMin,maxZoom:a.zoomMax})}i._esriAttributions.sort(function(t,e){return e.score-t.score}),N({target:i})}},this))}function N(t){var i=t.target,s=i._esriAttributions;if(i&&i.attributionControl){var r=i.attributionControl._container.querySelector(".esri-dynamic-attribution");if(r&&s){for(var n="",o=i.getBounds(),a=e.latLngBounds(o.getSouthWest().wrap(),o.getNorthEast().wrap()),u=i.getZoom(),l=0;l<s.length;l++){var h=s[l],p=h.attribution;!n.match(p)&&h.bounds.intersects(a)&&u>=h.minZoom&&u<=h.maxZoom&&(n+=", "+p)}n=n.substr(2),r.innerHTML=n,r.style.maxWidth=D(i),i.fire("attributionupdated",{attribution:n})}}}var Z={warn:q,cleanUrl:U,getUrlParams:k,isArcgisOnline:G,geojsonTypeToArcGIS:M,responseToFeatureCollection:F,geojsonToArcGIS:A,arcgisToGeoJSON:T,boundsToExtent:R,extentToBounds:w,calcAttributionWidth:D,setEsriAttribution:E,_setGeometry:z,_getAttributionData:B,_updateMapAttribution:N,_findIdAttributeFromFeature:C,_findIdAttributeFromResponse:P},j=e.Class.extend({options:{proxy:!1,useCors:i},generateSetter:function(t,i){return e.Util.bind(function(e){return this.params[t]=e,this},i)},initialize:function(t){if(t.request&&t.options?(this._service=t,e.Util.setOptions(this,t.options)):(e.Util.setOptions(this,t),this.options.url=U(t.url)),this.params=e.Util.extend({},this.params||{}),this.setters)for(var i in this.setters){var s=this.setters[i];this[i]=this.generateSetter(s,this)}},token:function(t){return this._service?this._service.authenticate(t):this.params.token=t,this},format:function(t){return this.params.returnUnformattedValues=!t,this},request:function(t,i){return this.options.requestParams&&e.Util.extend(this.params,this.options.requestParams),this._service?this._service.request(this.path,this.params,t,i):this._request("request",this.path,this.params,t,i)},_request:function(t,e,i,s,r){var n=this.options.proxy?this.options.proxy+"?"+this.options.url+e:this.options.url+e;return"get"!==t&&"request"!==t||this.options.useCors?d[t](n,i,s,r):d.get.JSONP(n,i,s,r)}});var W=j.extend({setters:{offset:"resultOffset",limit:"resultRecordCount",fields:"outFields",precision:"geometryPrecision",featureIds:"objectIds",returnGeometry:"returnGeometry",returnM:"returnM",transform:"datumTransformation",token:"token"},path:"query",params:{returnGeometry:!0,where:"1=1",outSR:4326,outFields:"*"},within:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelContains",this},intersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelIntersects",this},contains:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelWithin",this},crosses:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelCrosses",this},touches:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelTouches",this},overlaps:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelOverlaps",this},bboxIntersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelEnvelopeIntersects",this},indexIntersects:function(t){return this._setGeometryParams(t),this.params.spatialRel="esriSpatialRelIndexIntersects",this},nearby:function(t,i){return t=e.latLng(t),this.params.geometry=[t.lng,t.lat],this.params.geometryType="esriGeometryPoint",this.params.spatialRel="esriSpatialRelIntersects",this.params.units="esriSRUnit_Meter",this.params.distance=i,this.params.inSr=4326,this},where:function(t){return this.params.where=t,this},between:function(t,e){return this.params.time=[t.valueOf(),e.valueOf()],this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},orderBy:function(t,e){return e=e||"ASC",this.params.orderByFields=this.params.orderByFields?this.params.orderByFields+",":"",this.params.orderByFields+=[t,e].join(" "),this},run:function(t,e){return this._cleanParams(),this.options.isModern||G(this.options.url)?(this.params.f="geojson",this.request(function(i,s){this._trapSQLerrors(i),t.call(e,i,s,s)},this)):this.request(function(i,s){this._trapSQLerrors(i),t.call(e,i,s&&F(s),s)},this)},count:function(t,e){return this._cleanParams(),this.params.returnCountOnly=!0,this.request(function(e,i){t.call(this,e,i&&i.count,i)},e)},ids:function(t,e){return this._cleanParams(),this.params.returnIdsOnly=!0,this.request(function(e,i){t.call(this,e,i&&i.objectIds,i)},e)},bounds:function(t,e){return this._cleanParams(),this.params.returnExtentOnly=!0,this.request(function(i,s){s&&s.extent&&w(s.extent)?t.call(e,i,w(s.extent),s):(i={message:"Invalid Bounds"},t.call(e,i,null,s))},e)},distinct:function(){return this.params.returnGeometry=!1,this.params.returnDistinctValues=!0,this},pixelSize:function(t){var i=e.point(t);return this.params.pixelSize=[i.x,i.y],this},layer:function(t){return this.path=t+"/query",this},_trapSQLerrors:function(t){t&&"400"===t.code&&q("one common syntax error in query requests is encasing string values in double quotes instead of single quotes")},_cleanParams:function(){delete this.params.returnIdsOnly,delete this.params.returnExtentOnly,delete this.params.returnCountOnly},_setGeometryParams:function(t){this.params.inSr=4326;var e=z(t);this.params.geometry=e.geometry,this.params.geometryType=e.geometryType}});function J(t){return new W(t)}var Q=j.extend({setters:{contains:"contains",text:"searchText",fields:"searchFields",spatialReference:"sr",sr:"sr",layers:"layers",returnGeometry:"returnGeometry",maxAllowableOffset:"maxAllowableOffset",precision:"geometryPrecision",dynamicLayers:"dynamicLayers",returnZ:"returnZ",returnM:"returnM",gdbVersion:"gdbVersion",token:"token"},path:"find",params:{sr:4326,contains:!0,returnGeometry:!0,returnZ:!0,returnM:!1},layerDefs:function(t,e){return this.params.layerDefs=this.params.layerDefs?this.params.layerDefs+";":"",this.params.layerDefs+=[t,e].join(":"),this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},run:function(t,e){return this.request(function(i,s){t.call(e,i,s&&F(s),s)},e)}});function V(t){return new Q(t)}var H=j.extend({path:"identify",between:function(t,e){return this.params.time=[t.valueOf(),e.valueOf()],this}});var K=H.extend({setters:{layers:"layers",precision:"geometryPrecision",tolerance:"tolerance",returnGeometry:"returnGeometry"},params:{sr:4326,layers:"all",tolerance:3,returnGeometry:!0},on:function(t){var e=R(t.getBounds()),i=t.getSize();return this.params.imageDisplay=[i.x,i.y,96],this.params.mapExtent=[e.xmin,e.ymin,e.xmax,e.ymax],this},at:function(t){return 2===t.length&&(t=e.latLng(t)),this._setGeometryParams(t),this},layerDef:function(t,e){return this.params.layerDefs=this.params.layerDefs?this.params.layerDefs+";":"",this.params.layerDefs+=[t,e].join(":"),this},simplify:function(t,e){var i=Math.abs(t.getBounds().getWest()-t.getBounds().getEast());return this.params.maxAllowableOffset=i/t.getSize().y*e,this},run:function(t,e){return this.request(function(i,s){if(i)t.call(e,i,void 0,s);else{var r=F(s);s.results=s.results.reverse();for(var n=0;n<r.features.length;n++){r.features[n].layerId=s.results[n].layerId}t.call(e,void 0,r,s)}})},_setGeometryParams:function(t){var e=z(t);this.params.geometry=e.geometry,this.params.geometryType=e.geometryType}});function X(t){return new K(t)}var Y=H.extend({setters:{setMosaicRule:"mosaicRule",setRenderingRule:"renderingRule",setPixelSize:"pixelSize",returnCatalogItems:"returnCatalogItems",returnGeometry:"returnGeometry"},params:{returnGeometry:!1},at:function(t){return t=e.latLng(t),this.params.geometry=JSON.stringify({x:t.lng,y:t.lat,spatialReference:{wkid:4326}}),this.params.geometryType="esriGeometryPoint",this},getMosaicRule:function(){return this.params.mosaicRule},getRenderingRule:function(){return this.params.renderingRule},getPixelSize:function(){return this.params.pixelSize},run:function(t,e){return this.request(function(i,s){t.call(e,i,s&&this._responseToGeoJSON(s),s)},this)},_responseToGeoJSON:function(t){var e=t.location,i=t.catalogItems,s=t.catalogItemVisibilities,r={pixel:{type:"Feature",geometry:{type:"Point",coordinates:[e.x,e.y]},crs:{type:"EPSG",properties:{code:e.spatialReference.wkid}},properties:{OBJECTID:t.objectId,name:t.name,value:t.value},id:t.objectId}};if(t.properties&&t.properties.Values&&(r.pixel.properties.values=t.properties.Values),i&&i.features&&(r.catalogItems=F(i),s&&s.length===r.catalogItems.features.length))for(var n=s.length-1;n>=0;n--)r.catalogItems.features[n].properties.catalogItemVisibility=s[n];return r}});function $(t){return new Y(t)}var tt=e.Evented.extend({options:{proxy:!1,useCors:i,timeout:0},initialize:function(t){t=t||{},this._requestQueue=[],this._authenticating=!1,e.Util.setOptions(this,t),this.options.url=U(this.options.url)},get:function(t,e,i,s){return this._request("get",t,e,i,s)},post:function(t,e,i,s){return this._request("post",t,e,i,s)},request:function(t,e,i,s){return this._request("request",t,e,i,s)},metadata:function(t,e){return this._request("get","",{},t,e)},authenticate:function(t){return this._authenticating=!1,this.options.token=t,this._runQueue(),this},getTimeout:function(){return this.options.timeout},setTimeout:function(t){this.options.timeout=t},_request:function(t,i,s,r,n){this.fire("requeststart",{url:this.options.url+i,params:s,method:t},!0);var o=this._createServiceCallback(t,i,s,r,n);if(this.options.token&&(s.token=this.options.token),this.options.requestParams&&e.Util.extend(s,this.options.requestParams),!this._authenticating){var a=this.options.proxy?this.options.proxy+"?"+this.options.url+i:this.options.url+i;return"get"!==t&&"request"!==t||this.options.useCors?d[t](a,s,o,n):d.get.JSONP(a,s,o,n)}this._requestQueue.push([t,i,s,r,n])},_createServiceCallback:function(t,i,s,r,n){return e.Util.bind(function(o,a){!o||499!==o.code&&498!==o.code||(this._authenticating=!0,this._requestQueue.push([t,i,s,r,n]),this.fire("authenticationrequired",{authenticate:e.Util.bind(this.authenticate,this)},!0),o.authenticate=e.Util.bind(this.authenticate,this)),r.call(n,o,a),o?this.fire("requesterror",{url:this.options.url+i,params:s,message:o.message,code:o.code,method:t},!0):this.fire("requestsuccess",{url:this.options.url+i,params:s,response:a,method:t},!0),this.fire("requestend",{url:this.options.url+i,params:s,method:t},!0)},this)},_runQueue:function(){for(var t=this._requestQueue.length-1;t>=0;t--){var e=this._requestQueue[t];this[e.shift()].apply(this,e)}this._requestQueue=[]}});var et=tt.extend({identify:function(){return X(this)},find:function(){return V(this)},query:function(){return J(this)}});function it(t){return new et(t)}var st=tt.extend({query:function(){return J(this)},identify:function(){return $(this)}});function rt(t){return new st(t)}var nt=tt.extend({options:{idAttribute:"OBJECTID"},query:function(){return J(this)},addFeature:function(t,e,i){this.addFeatures(t,e,i)},addFeatures:function(t,e,i){for(var s=t.features?t.features:[t],r=s.length-1;r>=0;r--)delete s[r].id;return t=A(t),t=s.length>1?t:[t],this.post("addFeatures",{features:t},function(t,s){var r=s&&s.addResults?s.addResults.length>1?s.addResults:s.addResults[0]:void 0;e&&e.call(i,t||s.addResults[0].error,r)},i)},updateFeature:function(t,e,i){this.updateFeatures(t,e,i)},updateFeatures:function(t,e,i){var s=t.features?t.features:[t];return t=A(t,this.options.idAttribute),t=s.length>1?t:[t],this.post("updateFeatures",{features:t},function(t,s){var r=s&&s.updateResults?s.updateResults.length>1?s.updateResults:s.updateResults[0]:void 0;e&&e.call(i,t||s.updateResults[0].error,r)},i)},deleteFeature:function(t,e,i){this.deleteFeatures(t,e,i)},deleteFeatures:function(t,e,i){return this.post("deleteFeatures",{objectIds:t},function(t,s){var r=s&&s.deleteResults?s.deleteResults.length>1?s.deleteResults:s.deleteResults[0]:void 0;e&&e.call(i,t||s.deleteResults[0].error,r)},i)}});function ot(t){return new nt(t)}var at="https:"!==window.location.protocol?"http:":"https:",ut=e.TileLayer.extend({statics:{TILES:{Streets:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,subdomains:["server","services"],attribution:"USGS, NOAA",attributionUrl:"https://static.arcgis.com/attribution/World_Street_Map"}},Topographic:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,subdomains:["server","services"],attribution:"USGS, NOAA",attributionUrl:"https://static.arcgis.com/attribution/World_Topo_Map"}},Oceans:{urlTemplate:at+"//{s}.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],attribution:"USGS, NOAA",attributionUrl:"https://static.arcgis.com/attribution/Ocean_Basemap"}},OceansLabels:{urlTemplate:at+"//{s}.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Reference/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},NationalGeographic:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],attribution:"National Geographic, DeLorme, HERE, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, increment P Corp."}},DarkGray:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],attribution:"HERE, DeLorme, MapmyIndia, © OpenStreetMap contributors"}},DarkGrayLabels:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Reference/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},Gray:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],attribution:"HERE, DeLorme, MapmyIndia, © OpenStreetMap contributors"}},GrayLabels:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Reference/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:16,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},Imagery:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:22,maxNativeZoom:22,downsampled:!1,subdomains:["server","services"],attribution:"DigitalGlobe, GeoEye, i-cubed, USDA, USGS, AEX, Getmapping, Aerogrid, IGN, IGP, swisstopo, and the GIS User Community",attributionUrl:"https://static.arcgis.com/attribution/World_Imagery"}},ImageryLabels:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},ImageryTransportation:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},ShadedRelief:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:13,subdomains:["server","services"],attribution:"USGS"}},ShadedReliefLabels:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places_Alternate/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:12,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},Terrain:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:13,subdomains:["server","services"],attribution:"USGS, NOAA"}},TerrainLabels:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/Reference/World_Reference_Overlay/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:13,subdomains:["server","services"],pane:s?"esri-labels":"tilePane",attribution:""}},USATopo:{urlTemplate:at+"//{s}.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:15,subdomains:["server","services"],attribution:"USGS, National Geographic Society, i-cubed"}},ImageryClarity:{urlTemplate:at+"//clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,attribution:"Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community"}},Physical:{urlTemplate:at+"//{s}.arcgisonline.com/arcgis/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:8,subdomains:["server","services"],attribution:"U.S. National Park Service"}},ImageryFirefly:{urlTemplate:at+"//fly.maptiles.arcgis.com/arcgis/rest/services/World_Imagery_Firefly/MapServer/tile/{z}/{y}/{x}",options:{minZoom:1,maxZoom:19,attribution:"Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community",attributionUrl:"https://static.arcgis.com/attribution/World_Imagery"}}}},initialize:function(t,i){var s;if("object"==typeof t&&t.urlTemplate&&t.options)s=t;else{if("string"!=typeof t||!ut.TILES[t])throw new Error('L.esri.BasemapLayer: Invalid parameter. Use one of "Streets", "Topographic", "Oceans", "OceansLabels", "NationalGeographic", "Physical", "Gray", "GrayLabels", "DarkGray", "DarkGrayLabels", "Imagery", "ImageryLabels", "ImageryTransportation", "ImageryClarity", "ImageryFirefly", ShadedRelief", "ShadedReliefLabels", "Terrain", "TerrainLabels" or "USATopo"');s=ut.TILES[t]}var r=e.Util.extend(s.options,i);e.Util.setOptions(this,r),this.options.token&&-1===s.urlTemplate.indexOf("token=")&&(s.urlTemplate+="?token="+this.options.token),this.options.proxy&&(s.urlTemplate=this.options.proxy+"?"+s.urlTemplate),e.TileLayer.prototype.initialize.call(this,s.urlTemplate,r)},onAdd:function(t){E(t),"esri-labels"===this.options.pane&&this._initPane(),this.options.attributionUrl&&B((this.options.proxy?this.options.proxy+"?":"")+this.options.attributionUrl,t),t.on("moveend",N),-1!==this._url.indexOf("World_Imagery")&&t.on("zoomanim",lt,this),e.TileLayer.prototype.onAdd.call(this,t)},onRemove:function(t){t.off("moveend",N),e.TileLayer.prototype.onRemove.call(this,t)},_initPane:function(){if(!this._map.getPane(this.options.pane)){var t=this._map.createPane(this.options.pane);t.style.pointerEvents="none",t.style.zIndex=500}},getAttribution:function(){if(this.options.attribution)var t='<span class="esri-dynamic-attribution">'+this.options.attribution+"</span>";return t}});function lt(t){var i=t.target;if(i){var s=i.getZoom(),r=t.zoom,n=i.wrapLatLng(t.center);if(r>s&&r>13&&!this.options.downsampled){var o=i.project(n,r).divideBy(256).floor(),a=e.Util.template(this._url,e.Util.extend({s:this._getSubdomain(o),x:o.x,y:o.y,z:r},this.options)).replace(/tile/,"tilemap")+"/8/8";L.esri.request(a,{},function(t,e){if(!t)for(var i=0;i<e.data.length;i++){if(!e.data[i]){this.options.maxNativeZoom=r-1,this.options.downsampled=!0;break}this.options.maxNativeZoom=22}},this)}else r<13&&(this.options.downsampled=!1)}}var ht=e.TileLayer.extend({options:{zoomOffsetAllowance:.1,errorTileUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAAA1BMVEUzNDVszlHHAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAAAAAAAAAB6mUWpAAAADZJREFUeJztwQEBAAAAgiD/r25IQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7waBAAABw08RwAAAAABJRU5ErkJggg=="},statics:{MercatorZoomLevels:{0:156543.033928,1:78271.5169639999,2:39135.7584820001,3:19567.8792409999,4:9783.93962049996,5:4891.96981024998,6:2445.98490512499,7:1222.99245256249,8:611.49622628138,9:305.748113140558,10:152.874056570411,11:76.4370282850732,12:38.2185141425366,13:19.1092570712683,14:9.55462853563415,15:4.77731426794937,16:2.38865713397468,17:1.19432856685505,18:.597164283559817,19:.298582141647617,20:.14929107082381,21:.07464553541191,22:.0373227677059525,23:.0186613838529763}},initialize:function(t){t=k(t=e.Util.setOptions(this,t)),this.tileUrl=(t.proxy?t.proxy+"?":"")+t.url+"tile/{z}/{y}/{x}"+(t.requestParams&&Object.keys(t.requestParams).length>0?e.Util.getParamString(t.requestParams):""),-1!==t.url.indexOf("{s}")&&t.subdomains&&(t.url=t.url.replace("{s}",t.subdomains[0])),this.service=it(t),this.service.addEventParent(this),new RegExp(/tiles.arcgis(online)?\.com/g).test(t.url)&&(this.tileUrl=this.tileUrl.replace("://tiles","://tiles{s}"),t.subdomains=["1","2","3","4"]),this.options.token&&(this.tileUrl+="?token="+this.options.token),e.TileLayer.prototype.initialize.call(this,this.tileUrl,t)},getTileUrl:function(t){var i=this._getZoomForUrl();return e.Util.template(this.tileUrl,e.Util.extend({s:this._getSubdomain(t),x:t.x,y:t.y,z:this._lodMap&&this._lodMap[i]?this._lodMap[i]:i},this.options))},createTile:function(t,i){var s=document.createElement("img");return e.DomEvent.on(s,"load",e.Util.bind(this._tileOnLoad,this,i,s)),e.DomEvent.on(s,"error",e.Util.bind(this._tileOnError,this,i,s)),this.options.crossOrigin&&(s.crossOrigin=""),s.alt="",!this._lodMap||this._lodMap&&this._lodMap[this._getZoomForUrl()]?s.src=this.getTileUrl(t):this.once("lodmap",function(){s.src=this.getTileUrl(t)},this),s},onAdd:function(t){E(t),this._lodMap||this.metadata(function(i,s){if(!i&&s.spatialReference){var r=s.spatialReference.latestWkid||s.spatialReference.wkid;if(!this.options.attribution&&t.attributionControl&&s.copyrightText&&(this.options.attribution=s.copyrightText,t.attributionControl.addAttribution(this.getAttribution())),t.options.crs!==e.CRS.EPSG3857||102100!==r&&3857!==r)t.options.crs&&t.options.crs.code&&t.options.crs.code.indexOf(r)>-1||q("L.esri.TiledMapLayer is using a non-mercator spatial reference. Support may be available through Proj4Leaflet http://esri.github.io/esri-leaflet/examples/non-mercator-projection.html");else{this._lodMap={};for(var n=s.tileInfo.lods,o=ht.MercatorZoomLevels,a=0;a<n.length;a++){var u=n[a];for(var l in o){var h=o[l];if(this._withinPercentage(u.resolution,h,this.options.zoomOffsetAllowance)){this._lodMap[l]=u.level;break}}}this.fire("lodmap")}}},this),e.TileLayer.prototype.onAdd.call(this,t)},metadata:function(t,e){return this.service.metadata(t,e),this},identify:function(){return this.service.identify()},find:function(){return this.service.find()},query:function(){return this.service.query()},authenticate:function(t){var e="?token="+t;return this.tileUrl=this.options.token?this.tileUrl.replace(/\?token=(.+)/g,e):this.tileUrl+e,this.options.token=t,this.service.authenticate(t),this},_withinPercentage:function(t,e,i){return Math.abs(t/e-1)<i}});var pt=e.ImageOverlay.extend({onAdd:function(t){this._topLeft=t.getPixelBounds().min,e.ImageOverlay.prototype.onAdd.call(this,t)},_reset:function(){this._map.options.crs===e.CRS.EPSG3857?e.ImageOverlay.prototype._reset.call(this):e.DomUtil.setPosition(this._image,this._topLeft.subtract(this._map.getPixelOrigin()))}}),ct=e.Layer.extend({options:{opacity:1,position:"front",f:"image",useCors:i,attribution:null,interactive:!1,alt:""},onAdd:function(t){E(t),this.options.zIndex&&(this.options.position=null),this._update=e.Util.throttle(this._update,this.options.updateInterval,this),t.on("moveend",this._update,this),this._currentImage&&this._currentImage._bounds.equals(this._map.getBounds())?t.addLayer(this._currentImage):this._currentImage&&(this._map.removeLayer(this._currentImage),this._currentImage=null),this._update(),this._popup&&(this._map.on("click",this._getPopupData,this),this._map.on("dblclick",this._resetPopupState,this)),this.metadata(function(e,i){!e&&!this.options.attribution&&t.attributionControl&&i.copyrightText&&(this.options.attribution=i.copyrightText,t.attributionControl.addAttribution(this.getAttribution()))},this)},onRemove:function(t){this._currentImage&&this._map.removeLayer(this._currentImage),this._popup&&(this._map.off("click",this._getPopupData,this),this._map.off("dblclick",this._resetPopupState,this)),this._map.off("moveend",this._update,this)},bindPopup:function(t,i){return this._shouldRenderPopup=!1,this._lastClick=!1,this._popup=e.popup(i),this._popupFunction=t,this._map&&(this._map.on("click",this._getPopupData,this),this._map.on("dblclick",this._resetPopupState,this)),this},unbindPopup:function(){return this._map&&(this._map.closePopup(this._popup),this._map.off("click",this._getPopupData,this),this._map.off("dblclick",this._resetPopupState,this)),this._popup=!1,this},bringToFront:function(){return this.options.position="front",this._currentImage&&(this._currentImage.bringToFront(),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this.options.position="back",this._currentImage&&(this._currentImage.bringToBack(),this._setAutoZIndex(Math.min)),this},setZIndex:function(t){return this.options.zIndex=t,this._currentImage&&this._currentImage.setZIndex(t),this},_setAutoZIndex:function(t){if(this._currentImage){for(var e,i=this._currentImage.getPane().children,s=-t(-1/0,1/0),r=0,n=i.length;r<n;r++)e=i[r].style.zIndex,i[r]!==this._currentImage._image&&e&&(s=t(s,+e));isFinite(s)&&(this.options.zIndex=s+t(-1,1),this.setZIndex(this.options.zIndex))}},getAttribution:function(){return this.options.attribution},getOpacity:function(){return this.options.opacity},setOpacity:function(t){return this.options.opacity=t,this._currentImage&&this._currentImage.setOpacity(t),this},getTimeRange:function(){return[this.options.from,this.options.to]},setTimeRange:function(t,e){return this.options.from=t,this.options.to=e,this._update(),this},metadata:function(t,e){return this.service.metadata(t,e),this},authenticate:function(t){return this.service.authenticate(t),this},redraw:function(){this._update()},_renderImage:function(t,e,i){if(this._map){if(i&&(t="data:"+i+";base64,"+t),!t)return;var s=new pt(t,e,{opacity:0,crossOrigin:this.options.useCors,alt:this.options.alt,pane:this.options.pane||this.getPane(),interactive:this.options.interactive}).addTo(this._map),r=function(t){if(s.off("error",r,this),this._map){var i=t.target,n=this._currentImage;i._bounds.equals(e)&&i._bounds.equals(this._map.getBounds())?(this._currentImage=i,"front"===this.options.position?this.bringToFront():"back"===this.options.position&&this.bringToBack(),this.options.zIndex&&this.setZIndex(this.options.zIndex),this._map&&this._currentImage._map?this._currentImage.setOpacity(this.options.opacity):this._currentImage._map.removeLayer(this._currentImage),n&&this._map&&this._map.removeLayer(n),n&&n._map&&n._map.removeLayer(n)):this._map.removeLayer(i)}this.fire("load",{bounds:e})};s.once("error",function(){this._map.removeLayer(s),this.fire("error"),s.off("load",r,this)},this),s.once("load",r,this)}},_update:function(){if(this._map){var t=this._map.getZoom(),i=this._map.getBounds();if(!(this._animatingZoom||this._map._panTransition&&this._map._panTransition._inProgress))if(t>this.options.maxZoom||t<this.options.minZoom)this._currentImage&&(this._currentImage._map.removeLayer(this._currentImage),this._currentImage=null);else{var s=this._buildExportParams();e.Util.extend(s,this.options.requestParams),s?(this._requestExport(s,i),this.fire("loading",{bounds:i})):this._currentImage&&(this._currentImage._map.removeLayer(this._currentImage),this._currentImage=null)}}},_renderPopup:function(t,i,s,r){if(t=e.latLng(t),this._shouldRenderPopup&&this._lastClick.equals(t)){var n=this._popupFunction(i,s,r);n&&this._popup.setLatLng(t).setContent(n).openOn(this._map)}},_resetPopupState:function(t){this._shouldRenderPopup=!1,this._lastClick=t.latlng},_calculateBbox:function(){var t=this._map.getPixelBounds(),i=this._map.unproject(t.getBottomLeft()),s=this._map.unproject(t.getTopRight()),r=this._map.options.crs.project(s),n=this._map.options.crs.project(i),o=e.bounds(r,n);return[o.getBottomLeft().x,o.getBottomLeft().y,o.getTopRight().x,o.getTopRight().y].join(",")},_calculateImageSize:function(){var t=this._map.getPixelBounds(),e=this._map.getSize(),i=this._map.unproject(t.getBottomLeft()),s=this._map.unproject(t.getTopRight()),r=this._map.latLngToLayerPoint(s).y,n=this._map.latLngToLayerPoint(i).y;return(r>0||n<e.y)&&(e.y=n-r),e.x+","+e.y}}),mt=ct.extend({options:{updateInterval:150,format:"jpgpng",transparent:!0,f:"image"},query:function(){return this.service.query()},identify:function(){return this.service.identify()},initialize:function(t){t=k(t),this.service=rt(t),this.service.addEventParent(this),e.Util.setOptions(this,t)},setPixelType:function(t){return this.options.pixelType=t,this._update(),this},getPixelType:function(){return this.options.pixelType},setBandIds:function(t){return e.Util.isArray(t)?this.options.bandIds=t.join(","):this.options.bandIds=t.toString(),this._update(),this},getBandIds:function(){return this.options.bandIds},setNoData:function(t,i){return e.Util.isArray(t)?this.options.noData=t.join(","):this.options.noData=t.toString(),i&&(this.options.noDataInterpretation=i),this._update(),this},getNoData:function(){return this.options.noData},getNoDataInterpretation:function(){return this.options.noDataInterpretation},setRenderingRule:function(t){this.options.renderingRule=t,this._update()},getRenderingRule:function(){return this.options.renderingRule},setMosaicRule:function(t){this.options.mosaicRule=t,this._update()},getMosaicRule:function(){return this.options.mosaicRule},_getPopupData:function(t){var i=e.Util.bind(function(i,s,r){i||setTimeout(e.Util.bind(function(){this._renderPopup(t.latlng,i,s,r)},this),300)},this),s=this.identify().at(t.latlng);this.options.mosaicRule&&s.setMosaicRule(this.options.mosaicRule),s.run(i),this._shouldRenderPopup=!0,this._lastClick=t.latlng},_buildExportParams:function(){var t=parseInt(this._map.options.crs.code.split(":")[1],10),e={bbox:this._calculateBbox(),size:this._calculateImageSize(),format:this.options.format,transparent:this.options.transparent,bboxSR:t,imageSR:t};return this.options.from&&this.options.to&&(e.time=this.options.from.valueOf()+","+this.options.to.valueOf()),this.options.pixelType&&(e.pixelType=this.options.pixelType),this.options.interpolation&&(e.interpolation=this.options.interpolation),this.options.compressionQuality&&(e.compressionQuality=this.options.compressionQuality),this.options.bandIds&&(e.bandIds=this.options.bandIds),(0===this.options.noData||this.options.noData)&&(e.noData=this.options.noData),this.options.noDataInterpretation&&(e.noDataInterpretation=this.options.noDataInterpretation),this.service.options.token&&(e.token=this.service.options.token),this.options.renderingRule&&(e.renderingRule=JSON.stringify(this.options.renderingRule)),this.options.mosaicRule&&(e.mosaicRule=JSON.stringify(this.options.mosaicRule)),e},_requestExport:function(t,i){"json"===this.options.f?this.service.request("exportImage",t,function(t,e){t||(this.options.token&&(e.href+="?token="+this.options.token),this.options.proxy&&(e.href=this.options.proxy+"?"+e.href),this._renderImage(e.href,i))},this):(t.f="image",this._renderImage(this.options.url+"exportImage"+e.Util.getParamString(t),i))}});var dt=ct.extend({options:{updateInterval:150,layers:!1,layerDefs:!1,timeOptions:!1,format:"png24",transparent:!0,f:"json"},initialize:function(t){t=k(t),this.service=it(t),this.service.addEventParent(this),(t.proxy||t.token)&&"json"!==t.f&&(t.f="json"),e.Util.setOptions(this,t)},getDynamicLayers:function(){return this.options.dynamicLayers},setDynamicLayers:function(t){return this.options.dynamicLayers=t,this._update(),this},getLayers:function(){return this.options.layers},setLayers:function(t){return this.options.layers=t,this._update(),this},getLayerDefs:function(){return this.options.layerDefs},setLayerDefs:function(t){return this.options.layerDefs=t,this._update(),this},getTimeOptions:function(){return this.options.timeOptions},setTimeOptions:function(t){return this.options.timeOptions=t,this._update(),this},query:function(){return this.service.query()},identify:function(){return this.service.identify()},find:function(){return this.service.find()},_getPopupData:function(t){var i,s=e.Util.bind(function(i,s,r){i||setTimeout(e.Util.bind(function(){this._renderPopup(t.latlng,i,s,r)},this),300)},this);if((i=this.options.popup?this.options.popup.on(this._map).at(t.latlng):this.identify().on(this._map).at(t.latlng)).params.maxAllowableOffset||i.simplify(this._map,.5),this.options.popup&&this.options.popup.params&&this.options.popup.params.layers||(this.options.layers?i.layers("visible:"+this.options.layers.join(",")):i.layers("visible")),this.options.layerDefs&&"string"!=typeof this.options.layerDefs&&!i.params.layerDefs)for(var r in this.options.layerDefs)this.options.layerDefs.hasOwnProperty(r)&&i.layerDef(r,this.options.layerDefs[r]);i.run(s),this._shouldRenderPopup=!0,this._lastClick=t.latlng},_buildExportParams:function(){var t=parseInt(this._map.options.crs.code.split(":")[1],10),e={bbox:this._calculateBbox(),size:this._calculateImageSize(),dpi:96,format:this.options.format,transparent:this.options.transparent,bboxSR:t,imageSR:t};if(this.options.dynamicLayers&&(e.dynamicLayers=this.options.dynamicLayers),this.options.layers){if(0===this.options.layers.length)return;e.layers="show:"+this.options.layers.join(",")}return this.options.layerDefs&&(e.layerDefs="string"==typeof this.options.layerDefs?this.options.layerDefs:JSON.stringify(this.options.layerDefs)),this.options.timeOptions&&(e.timeOptions=JSON.stringify(this.options.timeOptions)),this.options.from&&this.options.to&&(e.time=this.options.from.valueOf()+","+this.options.to.valueOf()),this.service.options.token&&(e.token=this.service.options.token),this.options.proxy&&(e.proxy=this.options.proxy),this.options.disableCache&&(e._ts=Date.now()),e},_requestExport:function(t,i){"json"===this.options.f?this.service.request("export",t,function(t,e){t||(this.options.token&&e.href&&(e.href+="?token="+this.options.token),this.options.proxy&&e.href&&(e.href=this.options.proxy+"?"+e.href),e.href?this._renderImage(e.href,i):this._renderImage(e.imageData,i,e.contentType))},this):(t.f="image",this._renderImage(this.options.url+"export"+e.Util.getParamString(t),i))}});var ft=e.Layer.extend({options:{cellSize:512,updateInterval:150},initialize:function(t){t=e.setOptions(this,t),this._zooming=!1},onAdd:function(t){this._map=t,this._update=e.Util.throttle(this._update,this.options.updateInterval,this),this._reset(),this._update()},onRemove:function(){this._map.removeEventListener(this.getEvents(),this),this._removeCells()},getEvents:function(){return{moveend:this._update,zoomstart:this._zoomstart,zoomend:this._reset}},addTo:function(t){return t.addLayer(this),this},removeFrom:function(t){return t.removeLayer(this),this},_zoomstart:function(){this._zooming=!0},_reset:function(){this._removeCells(),this._cells={},this._activeCells={},this._cellsToLoad=0,this._cellsTotal=0,this._cellNumBounds=this._getCellNumBounds(),this._resetWrap(),this._zooming=!1},_resetWrap:function(){var t=this._map,e=t.options.crs;if(!e.infinite){var i=this._getCellSize();e.wrapLng&&(this._wrapLng=[Math.floor(t.project([0,e.wrapLng[0]]).x/i),Math.ceil(t.project([0,e.wrapLng[1]]).x/i)]),e.wrapLat&&(this._wrapLat=[Math.floor(t.project([e.wrapLat[0],0]).y/i),Math.ceil(t.project([e.wrapLat[1],0]).y/i)])}},_getCellSize:function(){return this.options.cellSize},_update:function(){if(this._map){var t=this._map.getPixelBounds(),i=this._getCellSize(),s=e.bounds(t.min.divideBy(i).floor(),t.max.divideBy(i).floor());this._removeOtherCells(s),this._addCells(s),this.fire("cellsupdated")}},_addCells:function(t){var i,s,r,n=[],o=t.getCenter(),a=this._map.getZoom();for(i=t.min.y;i<=t.max.y;i++)for(s=t.min.x;s<=t.max.x;s++)(r=e.point(s,i)).z=a,this._isValidCell(r)&&n.push(r);var u=n.length;if(0!==u)for(this._cellsToLoad+=u,this._cellsTotal+=u,n.sort(function(t,e){return t.distanceTo(o)-e.distanceTo(o)}),s=0;s<u;s++)this._addCell(n[s])},_isValidCell:function(t){var i=this._map.options.crs;if(!i.infinite){var s=this._cellNumBounds;if(!s)return!1;if(!i.wrapLng&&(t.x<s.min.x||t.x>s.max.x)||!i.wrapLat&&(t.y<s.min.y||t.y>s.max.y))return!1}if(!this.options.bounds)return!0;var r=this._cellCoordsToBounds(t);return e.latLngBounds(this.options.bounds).intersects(r)},_cellCoordsToBounds:function(t){var i=this._map,s=this.options.cellSize,r=t.multiplyBy(s),n=r.add([s,s]),o=i.wrapLatLng(i.unproject(r,t.z)),a=i.wrapLatLng(i.unproject(n,t.z));return e.latLngBounds(o,a)},_cellCoordsToKey:function(t){return t.x+":"+t.y},_keyToCellCoords:function(t){var i=t.split(":"),s=parseInt(i[0],10),r=parseInt(i[1],10);return e.point(s,r)},_removeOtherCells:function(t){for(var e in this._cells)t.contains(this._keyToCellCoords(e))||this._removeCell(e)},_removeCell:function(t){var e=this._activeCells[t];e&&(delete this._activeCells[t],this.cellLeave&&this.cellLeave(e.bounds,e.coords),this.fire("cellleave",{bounds:e.bounds,coords:e.coords}))},_removeCells:function(){for(var t in this._cells){var e=this._cells[t].bounds,i=this._cells[t].coords;this.cellLeave&&this.cellLeave(e,i),this.fire("cellleave",{bounds:e,coords:i})}},_addCell:function(t){this._wrapCoords(t);var e=this._cellCoordsToKey(t),i=this._cells[e];i&&!this._activeCells[e]&&(this.cellEnter&&this.cellEnter(i.bounds,t),this.fire("cellenter",{bounds:i.bounds,coords:t}),this._activeCells[e]=i),i||(i={coords:t,bounds:this._cellCoordsToBounds(t)},this._cells[e]=i,this._activeCells[e]=i,this.createCell&&this.createCell(i.bounds,t),this.fire("cellcreate",{bounds:i.bounds,coords:t}))},_wrapCoords:function(t){t.x=this._wrapLng?e.Util.wrapNum(t.x,this._wrapLng):t.x,t.y=this._wrapLat?e.Util.wrapNum(t.y,this._wrapLat):t.y},_getCellNumBounds:function(){var t=this._map.getPixelWorldBounds(),i=this._getCellSize();return t?e.bounds(t.min.divideBy(i).floor(),t.max.divideBy(i).ceil().subtract([1,1])):null}});function yt(t){this.values=[].concat(t||[])}yt.prototype.query=function(t){var e=this.getIndex(t);return this.values[e]},yt.prototype.getIndex=function(t){this.dirty&&this.sort();for(var e,i,s=0,r=this.values.length-1;s<=r;)if(e=(s+r)/2|0,+(i=this.values[Math.round(e)]).value<+t)s=e+1;else{if(!(+i.value>+t))return e;r=e-1}return Math.abs(~r)},yt.prototype.between=function(t,e){var i=this.getIndex(t),s=this.getIndex(e);if(0===i&&0===s)return[];for(;this.values[i-1]&&this.values[i-1].value===t;)i--;for(;this.values[s+1]&&this.values[s+1].value===e;)s++;return this.values[s]&&this.values[s].value===e&&this.values[s+1]&&s++,this.values.slice(i,s)},yt.prototype.insert=function(t){return this.values.splice(this.getIndex(t.value),0,t),this},yt.prototype.bulkAdd=function(t,e){return this.values=this.values.concat([].concat(t||[])),e?this.sort():this.dirty=!0,this},yt.prototype.sort=function(){return this.values.sort(function(t,e){return+e.value-+t.value}).reverse(),this.dirty=!1,this};var gt=ft.extend({options:{attribution:null,where:"1=1",fields:["*"],from:!1,to:!1,timeField:!1,timeFilterMode:"server",simplifyFactor:0,precision:6},initialize:function(t){if(ft.prototype.initialize.call(this,t),t=k(t),t=e.Util.setOptions(this,t),this.service=ot(t),this.service.addEventParent(this),"*"!==this.options.fields[0]){for(var i=!1,s=0;s<this.options.fields.length;s++)this.options.fields[s].match(/^(OBJECTID|FID|OID|ID)$/i)&&(i=!0);!1===i&&q("no known esriFieldTypeOID field detected in fields Array.  Please add an attribute field containing unique IDs to ensure the layer can be drawn correctly.")}this.options.timeField.start&&this.options.timeField.end?(this._startTimeIndex=new yt,this._endTimeIndex=new yt):this.options.timeField&&(this._timeIndex=new yt),this._cache={},this._currentSnapshot=[],this._activeRequests=0},onAdd:function(t){return E(t),this.service.metadata(function(e,i){if(!e){var s=i.supportedQueryFormats,r=!1;!1===this.service.options.isModern&&(r=!0),!r&&s&&-1!==s.indexOf("geoJSON")&&(this.service.options.isModern=!0),i.objectIdField&&(this.service.options.idAttribute=i.objectIdField),!this.options.attribution&&t.attributionControl&&i.copyrightText&&(this.options.attribution=i.copyrightText,t.attributionControl.addAttribution(this.getAttribution()))}},this),t.on("zoomend",this._handleZoomChange,this),ft.prototype.onAdd.call(this,t)},onRemove:function(t){return t.off("zoomend",this._handleZoomChange,this),ft.prototype.onRemove.call(this,t)},getAttribution:function(){return this.options.attribution},createCell:function(t,e){this._visibleZoom()&&this._requestFeatures(t,e)},_requestFeatures:function(t,i,s){return this._activeRequests++,1===this._activeRequests&&this.fire("loading",{bounds:t},!0),this._buildQuery(t).run(function(r,n,o){o&&o.exceededTransferLimit&&this.fire("drawlimitexceeded"),!r&&n&&n.features.length&&e.Util.requestAnimFrame(e.Util.bind(function(){this._addFeatures(n.features,i),this._postProcessFeatures(t)},this)),r||!n||n.features.length||this._postProcessFeatures(t),r&&this._postProcessFeatures(t),s&&s.call(this,r,n)},this)},_postProcessFeatures:function(t){this._activeRequests--,this._activeRequests<=0&&this.fire("load",{bounds:t})},_cacheKey:function(t){return t.z+":"+t.x+":"+t.y},_addFeatures:function(t,e){var i=this._cacheKey(e);this._cache[i]=this._cache[i]||[];for(var s=t.length-1;s>=0;s--){var r=t[s].id;-1===this._currentSnapshot.indexOf(r)&&this._currentSnapshot.push(r),-1===this._cache[i].indexOf(r)&&this._cache[i].push(r)}this.options.timeField&&this._buildTimeIndexes(t),this.createLayers(t)},_buildQuery:function(t){var i=this.service.query().intersects(t).where(this.options.where).fields(this.options.fields).precision(this.options.precision);return this.options.requestParams&&e.Util.extend(i.params,this.options.requestParams),this.options.simplifyFactor&&i.simplify(this._map,this.options.simplifyFactor),"server"===this.options.timeFilterMode&&this.options.from&&this.options.to&&i.between(this.options.from,this.options.to),i},setWhere:function(t,i,s){this.options.where=t&&t.length?t:"1=1";for(var r=[],n=[],o=0,a=null,u=e.Util.bind(function(t,u){if(t&&(a=t),u)for(var l=u.features.length-1;l>=0;l--)n.push(u.features[l].id);--o<=0&&this._visibleZoom()&&(this._currentSnapshot=n,e.Util.requestAnimFrame(e.Util.bind(function(){this.removeLayers(r),this.addLayers(n),i&&i.call(s,a)},this)))},this),l=this._currentSnapshot.length-1;l>=0;l--)r.push(this._currentSnapshot[l]);for(var h in this._activeCells){o++;var p=this._keyToCellCoords(h),c=this._cellCoordsToBounds(p);this._requestFeatures(c,h,u)}return this},getWhere:function(){return this.options.where},getTimeRange:function(){return[this.options.from,this.options.to]},setTimeRange:function(t,i,s,r){var n=this.options.from,o=this.options.to,a=0,u=null,l=e.Util.bind(function(e){e&&(u=e),this._filterExistingFeatures(n,o,t,i),a--,s&&a<=0&&s.call(r,u)},this);if(this.options.from=t,this.options.to=i,this._filterExistingFeatures(n,o,t,i),"server"===this.options.timeFilterMode)for(var h in this._activeCells){a++;var p=this._keyToCellCoords(h),c=this._cellCoordsToBounds(p);this._requestFeatures(c,h,l)}return this},refresh:function(){for(var t in this._activeCells){var e=this._keyToCellCoords(t),i=this._cellCoordsToBounds(e);this._requestFeatures(i,t)}this.redraw&&this.once("load",function(){this.eachFeature(function(t){this._redraw(t.feature.id)},this)},this)},_filterExistingFeatures:function(t,i,s,r){var n=t&&i?this._getFeaturesInTimeRange(t,i):this._currentSnapshot,o=this._getFeaturesInTimeRange(s,r);if(o.indexOf)for(var a=0;a<o.length;a++){var u=n.indexOf(o[a]);u>=0&&n.splice(u,1)}e.Util.requestAnimFrame(e.Util.bind(function(){this.removeLayers(n),this.addLayers(o)},this))},_getFeaturesInTimeRange:function(t,e){var i,s=[];if(this.options.timeField.start&&this.options.timeField.end){var r=this._startTimeIndex.between(t,e),n=this._endTimeIndex.between(t,e);i=r.concat(n)}else{if(!this._timeIndex)return q("You must set timeField in the layer constructor in order to manipulate the start and end time filter."),[];i=this._timeIndex.between(t,e)}for(var o=i.length-1;o>=0;o--)s.push(i[o].id);return s},_buildTimeIndexes:function(t){var e,i;if(this.options.timeField.start&&this.options.timeField.end){var s=[],r=[];for(e=t.length-1;e>=0;e--)i=t[e],s.push({id:i.id,value:new Date(i.properties[this.options.timeField.start])}),r.push({id:i.id,value:new Date(i.properties[this.options.timeField.end])});this._startTimeIndex.bulkAdd(s),this._endTimeIndex.bulkAdd(r)}else{var n=[];for(e=t.length-1;e>=0;e--)i=t[e],n.push({id:i.id,value:new Date(i.properties[this.options.timeField])});this._timeIndex.bulkAdd(n)}},_featureWithinTimeRange:function(t){if(!this.options.from||!this.options.to)return!0;var e=+this.options.from.valueOf(),i=+this.options.to.valueOf();if("string"==typeof this.options.timeField){var s=+t.properties[this.options.timeField];return s>=e&&s<=i}if(this.options.timeField.start&&this.options.timeField.end){var r=+t.properties[this.options.timeField.start],n=+t.properties[this.options.timeField.end];return r>=e&&r<=i||n>=e&&n<=i}},_visibleZoom:function(){if(!this._map)return!1;var t=this._map.getZoom();return!(t>this.options.maxZoom||t<this.options.minZoom)},_handleZoomChange:function(){if(this._visibleZoom())for(var t in this._activeCells){var e=this._activeCells[t].coords,i=this._cacheKey(e);this._cache[i]&&this.addLayers(this._cache[i])}else this.removeLayers(this._currentSnapshot),this._currentSnapshot=[]},authenticate:function(t){return this.service.authenticate(t),this},metadata:function(t,e){return this.service.metadata(t,e),this},query:function(){return this.service.query()},_getMetadata:function(t){this._metadata?t(void 0,this._metadata):this.metadata(e.Util.bind(function(e,i){this._metadata=i,t(e,this._metadata)},this))},addFeature:function(t,e,i){this.addFeatures(t,e,i)},addFeatures:function(t,i,s){this._getMetadata(e.Util.bind(function(r,n){if(r)i&&i.call(this,r,null);else{var o=t.features?t.features:[t];this.service.addFeatures(t,e.Util.bind(function(t,e){if(!t){for(var r=o.length-1;r>=0;r--)o[r].properties[n.objectIdField]=o.length>1?e[r].objectId:e.objectId,o[r].id=o.length>1?e[r].objectId:e.objectId;this.createLayers(o)}i&&i.call(s,t,e)},this))}},this))},updateFeature:function(t,e,i){this.updateFeatures(t,e,i)},updateFeatures:function(t,e,i){var s=t.features?t.features:[t];this.service.updateFeatures(t,function(t,r){if(!t){for(var n=s.length-1;n>=0;n--)this.removeLayers([s[n].id],!0);this.createLayers(s)}e&&e.call(i,t,r)},this)},deleteFeature:function(t,e,i){this.deleteFeatures(t,e,i)},deleteFeatures:function(t,e,i){return this.service.deleteFeatures(t,function(t,s){var r=s.length?s:[s];if(!t&&r.length>0)for(var n=r.length-1;n>=0;n--)this.removeLayers([r[n].objectId],!0);e&&e.call(i,t,s)},this)}}),vt=gt.extend({options:{cacheLayers:!0},initialize:function(t){gt.prototype.initialize.call(this,t),this._originalStyle=this.options.style,this._layers={}},onRemove:function(t){for(var e in this._layers)t.removeLayer(this._layers[e]),this.fire("removefeature",{feature:this._layers[e].feature,permanent:!1},!0);return gt.prototype.onRemove.call(this,t)},createNewLayer:function(t){var i=e.GeoJSON.geometryToLayer(t,this.options);return i&&(i.defaultOptions=i.options),i},_updateLayer:function(t,i){var s=[],r=this.options.coordsToLatLng||e.GeoJSON.coordsToLatLng;switch(i.properties&&(t.feature.properties=i.properties),i.geometry.type){case"Point":s=e.GeoJSON.coordsToLatLng(i.geometry.coordinates),t.setLatLng(s);break;case"LineString":s=e.GeoJSON.coordsToLatLngs(i.geometry.coordinates,0,r),t.setLatLngs(s);break;case"MultiLineString":case"Polygon":s=e.GeoJSON.coordsToLatLngs(i.geometry.coordinates,1,r),t.setLatLngs(s);break;case"MultiPolygon":s=e.GeoJSON.coordsToLatLngs(i.geometry.coordinates,2,r),t.setLatLngs(s)}},createLayers:function(t){for(var e=t.length-1;e>=0;e--){var i,s=t[e],r=this._layers[s.id];!this._visibleZoom()||!r||this._map.hasLayer(r)||this.options.timeField&&!this._featureWithinTimeRange(s)||(this._map.addLayer(r),this.fire("addfeature",{feature:r.feature},!0)),r&&this.options.simplifyFactor>0&&(r.setLatLngs||r.setLatLng)&&this._updateLayer(r,s),r||((i=this.createNewLayer(s))?(i.feature=s,i.addEventParent(this),this.options.onEachFeature&&this.options.onEachFeature(i.feature,i),this._layers[i.feature.id]=i,this.setFeatureStyle(i.feature.id,this.options.style),this.fire("createfeature",{feature:i.feature},!0),this._visibleZoom()&&(!this.options.timeField||this.options.timeField&&this._featureWithinTimeRange(s))&&this._map.addLayer(i)):q("invalid GeoJSON encountered"))}},addLayers:function(t){for(var e=t.length-1;e>=0;e--){var i=this._layers[t[e]];!i||this.options.timeField&&!this._featureWithinTimeRange(i.feature)||this._map.addLayer(i)}},removeLayers:function(t,e){for(var i=t.length-1;i>=0;i--){var s=t[i],r=this._layers[s];r&&(this.fire("removefeature",{feature:r.feature,permanent:e},!0),this._map.removeLayer(r)),r&&e&&delete this._layers[s]}},cellEnter:function(t,i){this._visibleZoom()&&!this._zooming&&this._map&&e.Util.requestAnimFrame(e.Util.bind(function(){var t=this._cacheKey(i),e=this._cellCoordsToKey(i),s=this._cache[t];this._activeCells[e]&&s&&this.addLayers(s)},this))},cellLeave:function(t,i){this._zooming||e.Util.requestAnimFrame(e.Util.bind(function(){if(this._map){var t=this._cacheKey(i),e=this._cellCoordsToKey(i),s=this._cache[t],r=this._map.getBounds();if(!this._activeCells[e]&&s){for(var n=!0,o=0;o<s.length;o++){var a=this._layers[s[o]];a&&a.getBounds&&r.intersects(a.getBounds())&&(n=!1)}n&&this.removeLayers(s,!this.options.cacheLayers),!this.options.cacheLayers&&n&&(delete this._cache[t],delete this._cells[e],delete this._activeCells[e])}}},this))},resetStyle:function(){return this.options.style=this._originalStyle,this.eachFeature(function(t){this.resetFeatureStyle(t.feature.id)},this),this},setStyle:function(t){return this.options.style=t,this.eachFeature(function(e){this.setFeatureStyle(e.feature.id,t)},this),this},resetFeatureStyle:function(t){var i=this._layers[t],s=this._originalStyle||e.Path.prototype.options;return i&&(e.Util.extend(i.options,i.defaultOptions),this.setFeatureStyle(t,s)),this},setFeatureStyle:function(t,e){var i=this._layers[t];return"function"==typeof e&&(e=e(i.feature)),i.setStyle&&i.setStyle(e),this},eachActiveFeature:function(t,e){if(this._map){var i=this._map.getBounds();for(var s in this._layers)-1!==this._currentSnapshot.indexOf(this._layers[s].feature.id)&&("function"==typeof this._layers[s].getLatLng&&i.contains(this._layers[s].getLatLng())?t.call(e,this._layers[s]):"function"==typeof this._layers[s].getBounds&&i.intersects(this._layers[s].getBounds())&&t.call(e,this._layers[s]))}return this},eachFeature:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getFeature:function(t){return this._layers[t]},bringToBack:function(){this.eachFeature(function(t){t.bringToBack&&t.bringToBack()})},bringToFront:function(){this.eachFeature(function(t){t.bringToFront&&t.bringToFront()})},redraw:function(t){return t&&this._redraw(t),this},_redraw:function(t){var i=this._layers[t],s=i.feature;if(i&&i.setIcon&&this.options.pointToLayer&&this.options.pointToLayer){var r=this.options.pointToLayer(s,e.latLng(s.geometry.coordinates[1],s.geometry.coordinates[0])).options.icon;i.setIcon(r)}if(i&&i.setStyle&&this.options.pointToLayer){var n=this.options.pointToLayer(s,e.latLng(s.geometry.coordinates[1],s.geometry.coordinates[0])).options;this.setFeatureStyle(s.id,n)}i&&i.setStyle&&this.options.style&&this.resetStyle(s.id)}});t.VERSION="2.3.1",t.Support=r,t.options=n,t.Util=Z,t.get=m,t.post=l,t.request=p,t.Task=j,t.task=function(t){return t=k(t),new j(t)},t.Query=W,t.query=J,t.Find=Q,t.find=V,t.Identify=H,t.identify=function(t){return new H(t)},t.IdentifyFeatures=K,t.identifyFeatures=X,t.IdentifyImage=Y,t.identifyImage=$,t.Service=tt,t.service=function(t){return t=k(t),new tt(t)},t.MapService=et,t.mapService=it,t.ImageService=st,t.imageService=rt,t.FeatureLayerService=nt,t.featureLayerService=ot,t.BasemapLayer=ut,t.basemapLayer=function(t,e){return new ut(t,e)},t.TiledMapLayer=ht,t.tiledMapLayer=function(t,e){return new ht(t,e)},t.RasterLayer=ct,t.ImageMapLayer=mt,t.imageMapLayer=function(t,e){return new mt(t,e)},t.DynamicMapLayer=dt,t.dynamicMapLayer=function(t,e){return new dt(t,e)},t.FeatureManager=gt,t.FeatureLayer=vt,t.featureLayer=function(t){return new vt(t)},Object.defineProperty(t,"__esModule",{value:!0})});
//# sourceMappingURL=esri-leaflet.js.map
;
define('js/view/VC_PlantSelection',[
    'classy',
    'text!templates/plant_selection_box.html',
    'signals',
		'leaflet',
			'esri-leaflet',
    'mustache',
    'simpleModal'
],
    function(Class, plantSelectionTemplate, signals, leaflet, esriLeaflet) {

        return Class.$extend({

            button: null,
            submitButtonClicked: null,
            closeButtonClicked: null,
            compareButtonClicked: null,
            resizeCallback: null,

            __init__: function(container) {
                var self = this;

                this.container = container;
                this.button = $('#compare_plants_button');
                this.toggleCompareClicked = new signals.Signal();
                this.submitButtonClicked = new signals.Signal();
                this.closeButtonClicked = new signals.Signal();

                this.button.click(function(e) {
                    if (!self.button.hasClass('disabled')) {
                        self.toggleCompareClicked.dispatch();
                    }
                });
                this.submitButtonHandler = function(event) {
                    self.submitButtonClicked.dispatch();
                };
                this.closeButtonHandler = function(event) {
                    self.closeButtonClicked.dispatch();
                };
            },
            loadForm: function(plants) {
                var i;
                // Load Data/Template
                var styles = [{
                    "submitDisabled": plants.length < 2 ? 'plant-select-submit-disabled' : null
                }];
                for(i = plants.length; i < 4; i++) {
                    plants.push({});
                }
                this.container.empty();
                this.container.html(Mustache.render(plantSelectionTemplate, {'plants':plants,'styles':styles}));

                for(i = 0; i < plants.length; i++) {
                    if(plants[i].code && plants[i].latLng) {

                        var map = leaflet.map('map-' + plants[i].code, {
                            center: plants[i].latLng,
                            zoom: 14,
                            zoomControl: false,
                            dragging : false,
                            boxZoom : false,
                            doubleClickZoom : false,
                            scrollWheelZoom : false,
                            keyboard : false
                        });
                        esriLeaflet.basemapLayer("Imagery").addTo(map);
                    }
                }
                // Attach event handlers
                this.container.find('#plant_select_dialog_submit_button').on('click', this.submitButtonHandler);
                this.container.find('#plant_select_dialog_close_button').on('click', this.closeButtonHandler);
            },
            showForm: function() {
                this.loadForm([]);
                $("#plant_selection_dialog").toggle(true);
            },
            hideForm: function() {
                $("#plant_selection_dialog").toggle(false);
            }
        });
    });

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_PlantListMap',[
	'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.MARKER, function(key, value) {
					var markerContainer = $('<span class="marker_container"></span>');
					$('<span class="map_legend_marker"></span>').appendTo(markerContainer)
						.css('background-color', value.color)
						.after('<span>' + value.name + '</span>');
					markerContainer.appendTo(innerLegendContainer);
					return true;
				});
			} else {
				this.options.legendTitle = null;
				this.$super();
			}
		},

		createMarkerOps: function() {
			return null;
		},

		__classvars__: {
			MARKER: {
				'WAS': { name: 'Biomass', color: 'rgb(93,151,50)' },
				'COW': { name: 'Coal', color: 'rgb(0,0,0)' },
				'GEO': { name: 'Geothermal', color: 'rgb(65,14,20)' },
				'HYC': { name: 'Hydro', color: 'rgb(0,57,83)' },
				'NG': { name: 'Natural gas', color: 'rgb(0,150,215)' },
				'NUC': { name: 'Nuclear', color: 'rgb(163,51,64)' },
				'OTH': { name: 'Other', color: 'rgb(51,51,51)' },
				'OOG': { name: 'Other fossil gases', color: 'rgb(242,183,124)' },
				'PET': { name: 'Petroleum', color: 'rgb(189,115,42)' },
				'HPS': { name: 'Pumped storage', color: 'rgb(102,136,152)' },
				'SUN': { name: 'Solar', color: 'rgb(255,199,2)' },
				'WND': { name: 'Wind', color: 'rgb(102,102,102)' },
				'WWW': { name: 'Wood', color: 'rgb(42,75,17)' }
			},

			styleMarkerPlant: function(marker) {
				marker.style = {
					fill: this.MARKER[marker.primaryFuel].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/PlantListPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/plantlist/ListConfigProxy',
	'js/model/plantlist/ListRequestProxy',
	'js/model/plantlist/ListParameterProxy',
	'js/model/plantlist/ListPlantTableDataProxy',
	'js/model/HelpTooltipProxy',
    // Mediators
	'js/view/MediatorPlantListDataGrid',
	'js/view/MediatorElectricityTopicSelection',
	'js/view/MediatorForm',
	'js/view/MediatorTab',
	'js/view/MediatorFrequencySelectionButton',
	'js/view/MediatorTimePeriodSelectionButton',
	'js/view/MediatorDownload',
	'js/view/MediatorSearch',
    'js/view/MediatorPlantSelection',
	'js/view/MediatorPlantListMap',
	'js/view/MediatorHelpTooltip',
	'js/view/VC_PageLayout',
	'js/view/VC_Form',
	'js/view/VC_Tabs',
	'js/view/VC_Frequency_Selection_Button',
	'js/view/VC_Time_Period_Selection_Button',
	'js/view/VC_Download',
	'js/view/VC_Search',
    'js/view/VC_PlantSelection',
	'js/view/VC_Selection',
	'js/view/VC_PlantListMap',
	'qTipsHelp',
	'js/view/VC_DataViewGrid',
	'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'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
    // Mediators
	DataGridMediator,
	TopicSelectionMediator,
	FormMediator,
	TabMediator,
	FrequencySelectionButtonMediator,
	TimePeriodSelectionButtonMediator,
	DownloadMediator,
	SearchMediator,
    PlantSelectionMediator,
	ListMapMediator,
	HelpTooltipMediator,
	TopicPageLayout,
	FormDialog,
	TabDialog,
	FrequencySelectionButtonDialog,
	TimePeriodSelectionButtonDialog,
	DownloadDialog,
	SearchBox,
    PlantSelectionDialog,
	TopicSelectionDialog,
	PlantListMap,
	HelpTooltipDialog,
	DataViewGrid,
	dateCodeUtils,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplate,
	mapTemplate,
	footnoteTemplate
) {
	var facade = null;

	///////////////
	// Shortcuts //
	///////////////


	//////////
	// Page //
	//////////

	var page = Page.$extend({
		load: function(request) {
			$('#pagecontent').addClass('plantList');
			this.$super(request);
			Page.clearErrorMessage();
			if (!facade) {
				facade = PlantListFacade.getInstance(this.name);
				facade.sendNotification(PlantListFacade.STARTUP, request);
			} else
				facade.sendNotification(PlantListFacade.RELOAD, request);
		},
		unload: function() {
			facade.sendNotification(PlantListFacade.UNLOAD);
			PlantListFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('plantList');
		}
	})('scripts/modules/pages/PlantListPage');

	////////////
	// Facade //
	////////////

	var PlantListFacade = 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.AUTO_REDIRECT, Controller.UpdateHashCommand);
			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, Controller.FrequencySelectionUpdatedSingleTimeSliderCommand);
			this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.TABLE_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.SEARCH_STRING_UPDATED, UpdatePlantListSearchCommand);
			this.registerCommand(this.$class.STATE_MAP_CLICKED, UpdateStateFilterCommand);
			this.registerCommand(this.$class.ERROR, Controller.ErrorCommand);
			this.registerCommand(this.$class.PLANT_LIST_CLICK_THROUGH, Controller.UpdatePlantHashCommand);
            this.registerCommand(this.$class.TAB_SELECTION_UPDATED, UpdateTabCommand);
            this.registerCommand(this.$class.PLANT_COMPARE_TOGGLE_CLICKED, ToggleSelectPlantCompareCommand);
            this.registerCommand(this.$class.PLANT_COMPARE_SELECTION_CLICKED, PlantCompareSelectionCommand);
            this.registerCommand(this.$class.PLANT_COMPARE_SUBMIT_CLICKED, UpdatePlantCompareHashCommand);
			this.registerCommand(this.$class.UNLOAD, ShutdownCommand);
		},

		__classvars__: {

			getInstance: function(key) {
				if (!puremvc.Facade.hasCore(key)) {
					new PlantListFacade(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 ShutdownCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand: function() {
			this.addSubCommand(RemoveViewCommand);
			this.addSubCommand(RemoveModelCommand);
		}
	});

	var RemoveViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeMediator(PageMediator.NAME);
		}
	});

	var RemoveModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeProxy(ConfigProxy.NAME);
			this.facade.removeProxy(RequestProxy.NAME);
			this.facade.removeProxy(ParameterProxy.NAME);
			this.facade.removeProxy(TableDataProxy.NAME);
			this.facade.removeProxy(HelpTooltipProxy.NAME);
		}
	});

	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute: function(note) {
			var requestProxy = new RequestProxy(note.getBody());
			var configProxy = new ConfigProxy();

			var dataParams = {
				label: 'Data',
				children: [{
					id: 'csv_table',
					type: 'csv',
					class: 'ico_csv',
					target: 'table',
					label: 'Table (CSV)'
				}]
			};

			var downloadParams = $.extend([], [{
					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)'
					}]
				},
				dataParams
			]);

			configProxy.setDownloadParameters(downloadParams);
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();


			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);

		}
	});

	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 UpdatePlantListSearchCommand = 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 = "",
				aggColumnId;

			var topic = configProxy.getTopic(parameterProxy.getTopicId());

			for (var i = 0; i < topic.aggColumns.length; i++) {
				if (topic.aggColumns[i].filterGroup.varName == 'geo') {
					aggColumnId = topic.aggColumns[i].aggColumnId;
					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;

			var currentAgg = parameterProxy.getAgg();
			if(typeof currentAgg !== 'string' || currentAgg === '') {
				filter.agg = aggColumnId;
			}
			else if (currentAgg.indexOf(aggColumnId) === -1){
				var aggArray = currentAgg.split(',');
				aggArray.push(aggColumnId);
				filter.agg = aggArray.join(',');
			}



			this.sendNotification(Facade.TABLE_SELECTION_UPDATED, filter, 'StateMapClicked');
		}
	});

    // Plant Compare Selection Commands

    var ToggleSelectPlantCompareCommand = puremvc.SimpleCommand.$extend({
        execute: function(note) {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
            tableDataProxy.resetPlantSelectData(parameterProxy.getPlants());
            parameterProxy.togglePlantCompare();
            this.sendNotification(Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED);
        }
    });

    var PlantCompareSelectionCommand = puremvc.SimpleCommand.$extend({
        execute: function(note) {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME);
            parameterProxy.updatePlantCompareSelection(note.getBody());
            tableDataProxy.updatePlantSelectData(note.getBody());
            this.sendNotification(Facade.PLANT_COMPARE_SELECTION_UPDATED);
        }
    });

    var UpdatePlantCompareHashCommand = puremvc.SimpleCommand.$extend({
        execute: function(note) {
            var paramsObj = note.getBody(),
                parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                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('plant/compare/' + paramsObj.codes + '/?freq=' + paramsObj.freq + '&datecode=' + paramsObj.datecode);
        }
    });

    var UpdateTabCommand = puremvc.MacroCommand.$extend({
        initializeMacroCommand: function() {
            this.addSubCommand(ResetPlantSelectCommand);
            this.addSubCommand(Controller.UpdateHashCommand);
        }
    });

    var ResetPlantSelectCommand = puremvc.SimpleCommand.$extend({
        execute: function(note) {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            parameterProxy.resetShowPlantCompare();
            parameterProxy.resetPlants();
            this.sendNotification(Facade.PLANT_COMPARE_TOGGLE_PARAM_UPDATED);
        }
    });

	///////////////
	// 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),
                requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
                topic = configProxy.getTopic(requestProxy.getTopicId());

			pageLayout.getPinKeyVisualizationContainer().css('display', 'none');

			var formDialog = new FormDialog(pageLayout.getFormContainer());
			var tabDialog = new TabDialog(pageLayout.getTabContainer());
			var frequencySelectionButtonDialog = new FrequencySelectionButtonDialog(pageLayout.getFrequencyContainer());
			var timePeriodSelectionButtonDialog = new TimePeriodSelectionButtonDialog(pageLayout.getTimePeriodContainer(), topic);
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var searchBox = new SearchBox(pageLayout.getSearchContainer());
            var plantSelectionDialog = new PlantSelectionDialog(pageLayout.getPlantSelectionContainer());
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var dataGrid = new DataViewGrid(pageLayout.getTableContainer());
			var helpTooltipDialog = new HelpTooltipDialog();
			var listMap = new PlantListMap(pageLayout.getChartContainer(), 'us_merc_en');


			var formMediator = new FormMediator(formDialog);
			var tabMediator = new TabMediator(tabDialog);
			var frequencySelectionButtonMediator = new FrequencySelectionButtonMediator(frequencySelectionButtonDialog);
			var timePeriodSelectionButtonMediator = new TimePeriodSelectionButtonMediator(timePeriodSelectionButtonDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var searchMediator = new SearchMediator(searchBox);
            var plantSelectionMediator = new PlantSelectionMediator(plantSelectionDialog);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, true);
			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(tabMediator);
			this.facade.registerMediator(frequencySelectionButtonMediator);
			this.facade.registerMediator(timePeriodSelectionButtonMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(searchMediator);
            this.facade.registerMediator(plantSelectionMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(listMapMediator);

			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, { rseAvailable: false, topic: requestProxy.getTopicId(), showFilterValues: true, showDetail: true }));

			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({
                autoDimensions : false,
                autoScale : false,
                autoHeight : true,
                autoWidth : false,
                autoSize : false,
                fitToView : true,
                scrolling : 'auto',
                type : 'ajax'
			});

		},

		listNotificationInterests: function() {
			return this.$super().concat([
				PlantListFacade.TABLEDATA_UPDATED,
				PlantListFacade.TABLE_RENDERED
			]);
		},

		handleNotification: function(note) {
			var vc;
			switch (note.getName()) {
				case PlantListFacade.TABLEDATA_UPDATED:
					vc = this.getViewComponent();
					var tableDataProxy = this.facade.retrieveProxy(TableDataProxy.NAME),
						parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
						configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
						titleArray = tableDataProxy.getTableTitle().split(','),

					//Only displaying the first 5 filters
						tempTitle = titleArray.slice(0,5);

					//Adding ellipses if more that 5 filters are selected
					if(titleArray.length > 5){
						tempTitle.push('...');
					}
					var pageTitle = tempTitle.join(', ');

					this.getViewComponent().getTitleContainer().empty();
					this.getViewComponent().getTitleContainer().css('display', 'block');
					this.getViewComponent().getTitleContainer().append(Mustache.to_html(tableTitleTemplate, { title: pageTitle }));

					//Adds title attribute with full list of filters when user hovers over table title.
					this.getViewComponent().getTitleContainer().find('h2').attr('title', titleArray.join(', '));

					window.document.title = 'Electricity data browser - ' + titleArray.join(', ');
					break;
				case PlantListFacade.TABLE_RENDERED:
					vc = this.getViewComponent();
					if (vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},

		onRemove : function() {

			this.facade.removeMediator(HelpTooltipMediator.NAME);
			this.facade.removeMediator(ListMapMediator.NAME);
			this.facade.removeMediator(DataGridMediator.NAME);
			this.facade.removeMediator(TopicSelectionMediator.NAME);
			this.facade.removeMediator(PlantSelectionMediator.NAME);
			this.facade.removeMediator(SearchMediator.NAME);
			this.facade.removeMediator(DownloadMediator.NAME);
			this.facade.removeMediator(TimePeriodSelectionButtonMediator.NAME);
			this.facade.removeMediator(FrequencySelectionButtonMediator.NAME);
			this.facade.removeMediator(TabMediator.NAME);
			this.facade.removeMediator(FormMediator.NAME);
		},

		__classvars__: {
			NAME: 'PageMediator'
		}

	});


	/////////////////////
	// View Components //
	/////////////////////

	var PageLayout = TopicPageLayout.$extend({

		__init__: function() {
			this.$super($('#pagecontent'), {
				'mainFilterButtonOpts': {
					'selected': true,
					'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'
				},
				'searchPlantsButtonOpts': {
					'selected': false,
					'id': 'search_plants_button'
				},
				'comparePlantsButtonOpts': {
					'selected': false,
					'id': 'compare_plants_button'
				},
				'uiRadioOpts': {
					'selected': false,
					'id': 'ui-radio'
				},
				showTabs : true,
				showSearch : true
			});
			this.getChartContainer().html(Mustache.render(mapTemplate));
		}
	});

	return page;
});

define('js/model/mixins/ConfigMixinPreviousPage',[], function() {
	return {
		getPreviousPage: function() {
			return this.getData().previousPage;
		},
		setPreviousPage: function(value) {
			this.getData().previousPage = value;
		}
	};
});

define('js/model/plantdata/SiteDataConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/mixins/ConfigMixinVisualization',
	'js/model/mixins/ConfigMixinDownload',
	'js/model/mixins/ConfigMixinViewStack',
	'js/model/mixins/ConfigMixinLinkType',
	'js/model/mixins/ConfigMixinPreviousPage',
    'js/model/mixins/ConfigMixinTabs'
], function(
	ConfigProxy,
	ConfigMixinVisualization,
	ConfigMixinDownload,
	ConfigMixinViewStack,
	ConfigMixinLinkType,
	ConfigMixinPreviousPage,
    ConfigMixinTabs
) {
	return ConfigProxy.$extend({
		__include__: [
			ConfigMixinVisualization,
			ConfigMixinDownload,
			ConfigMixinViewStack,
			ConfigMixinLinkType,
			ConfigMixinPreviousPage,
            ConfigMixinTabs
		]
	});
});

define('js/model/plantdata/SiteDataRequestProxy',[
	'js/model/RequestProxy',
	'js/model/mixins/RequestMixinTopic'
], function(
	RequestProxy,
	RequestMixinTopic
) {
	return RequestProxy.$extend({
		__include__: [
			RequestMixinTopic
		],

		getId: function() {
			return this.getData()['1'];
		}
	});
});

define('js/model/mixins/ParameterMixinSite',[], function() {
	return {
		getId: function() {
			return this.getData().id;
		},

		updateId: function(id) {
			$.extend(this.getData(), {
				'id': id
			});
		}
	};
});

define('js/model/plantdata/SiteDataParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/mixins/ParameterMixinSite',
	'js/model/mixins/ParameterMixinTopic',
	'js/model/mixins/ParameterMixinReport',
	'js/model/mixins/ParameterMixinFrequency',
	'js/model/mixins/ParameterMixinStartEnd',
	'js/model/mixins/ParameterMixinChart',
	'js/model/mixins/ParameterMixinLinkType',
    'js/model/mixins/ParameterMixinTab'
], function(
	Facade,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	ParameterMixinSite,
	ParameterMixinTopic,
	ParameterMixinReport,
	ParameterMixinFrequency,
	ParameterMixinStartEnd,
	ParameterMixinChart,
	ParameterMixinLinkType,
    ParameterMixinTab
) {
	return ParameterProxy.$extend({

		__include__: [
			ParameterMixinSite,
			ParameterMixinTopic,
			ParameterMixinReport,
			ParameterMixinFrequency,
			ParameterMixinStartEnd,
			ParameterMixinChart,
			ParameterMixinLinkType,
            ParameterMixinTab
		],

		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',
				'tab'
			];
		},


		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'),
                'tab' : this.getTab()
			};
		},

		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,
				frequencyParamUpdated = false,
                tabParamsUpdated = !('tab' in requestProxy.getQuery()),
				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 |= frequencyParamUpdated |= 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';
                // Check and see if our tab  has changed
                tabParamsUpdated |= tmpKey == 'tab';
                // Check to see if tabs have changed
                tableParamsUpdated |= tabParamsUpdated;
			});

			this.updateChartIdFrequencies(this.getFreq());

            if(tabParamsUpdated) {
                this.sendNotification(Facade.TAB_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 (chartParamsUpdated)
				this.sendNotification(Facade.CHART_PARAMS_UPDATED);
			if (frequencyParamUpdated)
				this.sendNotification(Facade.FREQUENCY_PARAM_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());

            var tabs = [];

            $('input[name="tabs"]').each(function() {
                var tabValue = $(this).val();
                tabs.push(tabValue);
                switch(tabValue) {
                    case 'water':
                        $('input[name="water"]').each(function() {
                            tabs.push(tabValue + '-' + $(this).val());
                        });
                        break;
                    case 'consumption':
                        $('input[name="consumption"]').each(function() {
                            tabs.push(tabValue + '-' + $(this).val());
                        });
                        break;
                    default:
                        tabs.push(tabValue);
                        break;
                }
            });


           this.validateTab(vals, topic, tabs);
		},

		__classvars__: {
			NAME: 'ParameterProxy'
		}
	});
});

define('js/model/plantdata/SiteDataTableDataProxy',[
	'js/Facade',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy',
	'js/model/mixins/TableDataMixinLevel',
	'js/model/mixins/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;
		},

        getCombinedTableDataByType: function() {
            var ret = [];
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            var dataType = parameterProxy.getTab();
            var seriesData = this.getSeriesData();
            var tableTitle = this.getTableTitle();
            var tableData = this.getTableDataByType(dataType);

            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;
        },

        getTableDataByType: function(dataType) {
            var ret = [];
            var tableData = this.getData().DATA;
            for (var i = 0; i < tableData.length; i++) {
                if (tableData[i].DISPLAY_DATA_TYPE == dataType)
                    ret.push(tableData[i]);
            }
            return ret;
        },

		getTableTitle: function() {
			return this.getInfo().NAME;
		},

        getVisualizationTitle: function() {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            var title = this.getInfo().NAME;
            switch (parameterProxy.getTab()) {
                case "generation":
                    return title+' net generation';
                case "consumption":
                    return title+' fuel consumption quantity';
                case "water":
                    return title+' water usage';
                case "annual_emissions":
                    return title+' annual emissions';
                default:
                    return title+' overview';
            }
        },

		getInfo: function() {
			return this.getData().INFO;
		},

		getChildRows: function(row) {
			var ret = [];
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
            var combinedData = this.getCombinedTableDataByType();

			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 parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME);
			var childRows = this.getChildRows(row);
            var combinedData = this.getCombinedTableDataByType();
			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/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:
                case Facade.CHART_DATA_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.getCombinedTableDataByType(),
						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),
				sourcekey = parameterProxy.getLinkType(),
				columns = [this.getPinSourcekeyColumn(sourcekey),  {
					'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/plantDataTableTitle.html',[],function () { return '<div>\r\n\t{{#BACKURL}}<a href="./#/{{BACKURL}}">< Return to previous page</a>{{/BACKURL}}\r\n\t<br/>\r\n\t<h2>{{NAME}}</h2>\r\n\t<strong>Plant id:</strong> {{ID}}  |  <strong>Operator:</strong> {{UTILITYNAME}}  |  <strong>Operator code:</strong> {{UTILITYCODE}}\r\n\t<br/>\r\n\t<strong>State:</strong> {{STATE}}  |  <strong>Sector:</strong> {{SECTOR}} {{#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<!--\r\n\t |  Coal shipments to {{NAME}}-->\r\n</div>\r\n\r\n';});

define('js/pages/PlantDataPage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
	'js/model/plantdata/SiteDataConfigProxy',
	'js/model/plantdata/SiteDataRequestProxy',
	'js/model/plantdata/SiteDataParameterProxy',
	'js/model/plantdata/SiteDataTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/view/VC_PageLayout',
    'js/view/VC_Tabs',
	'js/view/VC_Download',
	'js/view/VC_Visualization',
	'js/view/VC_Selection',
	'js/view/VC_Frequency_Selection_Button',
	'js/view/VC_Time_Period_Range_Selection_Button',
	'datagrid',
	'viewStack',
	'js/view/VC_ColumnChart',
	'js/view/VC_LineChart',
	'qTipsHelp',
    // Mediators
    'js/view/MediatorTab',
	'js/view/MediatorDownload',
	'js/view/MediatorVisualization',
	'js/view/MediatorElectricityTopicSelection',
	'js/view/MediatorFrequencySelectionButton',
	'js/view/MediatorTimePeriodRangeSelectionButton',
	'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/plantDataTableTitle.html',
	'text!templates/footnotes.html'
], function(
	hasher,
	signals,
	puremvc,
	Class,
	Page,
	Facade,
	Controller,
	ConfigProxy,
	RequestProxy,
	ParameterProxy,
	TableDataProxy,
	HelpTooltipProxy,
	TopicPageLayout,
    TabDialog,
	DownloadDialog,
	VisualizationDialog,
	TopicSelectionDialog,
	FrequencySelectionButtonDialog,
	TimePeriodSelectionButtonDialog,
	DataGrid,
	ViewStack,
	ColumnChart,
	LineChart,
	HelpTooltipDialog,
    // Mediators
    TabMediator,
	DownloadMediator,
	VisualizationMediator,
	TopicSelectionMediator,
	FrequencySelectionButtonMediator,
	TimePeriodSelectionButtonMediator,
	DataGridMediator,
	ViewStackMediator,
	LineChartMediator,
	ColumnChartMediator,
	HelpTooltipMediator,
	PinKeyVisualizationMediator,
	pageTemplate,
	filterButtonTemplate,
	downloadButtonTemplate,
	tableTitleTemplate,
	footnoteTemplate
) {
	var facade = null;

	var page = Page.$extend({
		load: function(request) {
			request["0"] = 1;
			$('#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');
		}
	})('scripts/modules/pages/PlantDataPage');

	////////////
	// 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);
			this.registerCommand(this.$class.UNLOAD, ShutdownCommand);
            this.registerCommand(this.$class.TAB_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 ShutdownCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand: function() {
			this.addSubCommand(RemoveViewCommand);
			this.addSubCommand(RemoveModelCommand);
		}
	});

	var RemoveViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeMediator(PageMediator.NAME);
		}
	});

	var RemoveModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeProxy(ConfigProxy.NAME);
			this.facade.removeProxy(RequestProxy.NAME);
			this.facade.removeProxy(ParameterProxy.NAME);
			this.facade.removeProxy(TableDataProxy.NAME);
			this.facade.removeProxy(HelpTooltipProxy.NAME);
		}
	});

	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'
							/*,
							                                    iframe_link: function() {
							                                        var params = self.facade.retrieveMediator(LineChartMediator.NAME).getViewComponent().chart.getEmbedParams();
							                                        return 'http://www.eia.gov/opendata/iframe.php?' + params;
							                                    }*/
					}]
				}, {
					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 tabOptions = $.extend(true, {}, TabDialog.defaultConfig.tabOptions);
            tabOptions.tabs.tabList = tabOptions.tabs.tabList.filter(function (opt) {
                return opt.id !== 'overview';
            });
            delete tabOptions.subTabs;
            configProxy.setTabOptions(tabOptions);

			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();

			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
		}
	});

	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);
		}
	});


	///////////////
	// 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),
					requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
					topic = configProxy.getTopic(requestProxy.getTopicId());

            var tabDialog = new TabDialog(pageLayout.getTabContainer(), { tabOptions: configProxy.getTabOptions() });
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());
			var visualizationDialog = new VisualizationDialog(pageLayout.getVisualizationContainer(), { items: configProxy.getVisualizations() });
			var middleTopicSelectionDialog = new TopicSelectionDialog(pageLayout.getMiddleTopicSelectionContainer(), false);
			var frequencySelectionButtonDialog = new FrequencySelectionButtonDialog(pageLayout.getFrequencyContainer());
			var timePeriodSelectionButtonDialog = new TimePeriodSelectionButtonDialog(pageLayout.getTimePeriodContainer(), topic);
			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 tabMediator = new TabMediator(tabDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);
			var visualizationMediator = new VisualizationMediator(visualizationDialog);
			var middleTopicSelectionMediator = new TopicSelectionMediator(middleTopicSelectionDialog, true);
			var frequencySelectionButtonMediator = new FrequencySelectionButtonMediator(frequencySelectionButtonDialog);
			var timePeriodSelectionButtonMediator = new TimePeriodSelectionButtonMediator(timePeriodSelectionButtonDialog);
			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(tabMediator);
			this.facade.registerMediator(helpTooltipMediator);
			this.facade.registerMediator(downloadMediator);
			this.facade.registerMediator(visualizationMediator);
			this.facade.registerMediator(middleTopicSelectionMediator);
			this.facade.registerMediator(frequencySelectionButtonMediator);
			this.facade.registerMediator(timePeriodSelectionButtonMediator);
			this.facade.registerMediator(viewStackMediator);
			this.facade.registerMediator(dataGridMediator);
			this.facade.registerMediator(lineChartMediator);
			this.facade.registerMediator(columnChartMediator);
			this.facade.registerMediator(pinKeyVisualizationMediator);

			this.getViewComponent().getFootnoteContainer()
				.empty()
				.html(Mustache.to_html(footnoteTemplate, { rseAvailable: false, topic: requestProxy.getTopicId(), showFilterValues: false, showDetail: true }));

			$('a.lightbox', this.getViewComponent().getFootnoteContainer()).fancybox({
                autoDimensions : false,
                autoScale : false,
                autoHeight : true,
                autoWidth : false,
                autoSize : false,
                fitToView : true,
                scrolling : 'auto',
                type : 'ajax'
			});


		},

		listNotificationInterests: function() {
			return this.$super().concat([
				PlantDataFacade.TABLEDATA_UPDATED,
				PlantDataFacade.TABLE_RENDERED
			]);
		},

		handleNotification: function(note) {
			var vc = this.getViewComponent();
			switch (note.getName()) {
				case PlantDataFacade.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);

					//var linkArgs = parameterProxy.getQueryString();
					//var tableInfo = $.extend(tableDataProxy.getInfo(), { BACKURL: (prevPage ? prevPage.request_ : parameterProxy.getFullTopicPath({}, parameterProxy.getTopicId())), LINK_ARGS: linkArgs });

					//vc.getTitleContainer().append(Mustache.to_html(tableTitleTemplate, tableInfo));

					window.document.title = 'Electricity data browser - ' + tableDataProxy.getTableTitle();

					break;
				case PlantDataFacade.TABLE_RENDERED:
					if (vc.getMiddleTopicSelectionContainer().is(':hidden'))
						vc.getMiddleTopicSelectionContainer().slideDown(1500);
					break;
				default:
					this.$super(note);
					break;
			}
		},

		onRemove : function() {

			this.facade.removeMediator(PinKeyVisualizationMediator.NAME);
			this.facade.removeMediator(HelpTooltipMediator.NAME);
			this.facade.removeMediator(ColumnChartMediator.NAME);
			this.facade.removeMediator(LineChartMediator.NAME);
			this.facade.removeMediator(ViewStackMediator.NAME);
			this.facade.removeMediator(DataGridMediator.NAME);
			this.facade.removeMediator(TimePeriodSelectionButtonMediator.NAME);
			this.facade.removeMediator(FrequencySelectionButtonMediator.NAME);
			this.facade.removeMediator(TopicSelectionMediator.NAME);
			this.facade.removeMediator(VisualizationMediator.NAME);
			this.facade.removeMediator(DownloadMediator.NAME);
		},

		getMapUrl: function(vc, tableDataProxy, prevPage, parameterProxy, template){
			//perform ajax call to layer-config.php script.
			let mapId = 'electricity-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;

					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(tableTitleTemplate, tableInfo));	
					//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})));

						
					}
				}
			);
			//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'
				},
				'searchPlantsButtonOpts': {
					'selected': false,
					'id': 'search_plants_button'
				},
				'comparePlantsButtonOpts': {
					'selected': false,
					'id': 'compare_plants_button'
				},
				'uiRadioOpts': {
					'selected': false,
					'id': 'ui-radio'
				},
				'timePeriodSelectionButtonOpts': {
					'selected': false
				},
				'frequencySelectionButtonOpts': {
					'selected': false
				},
				showTabs : true,
				showSearch : false
			});
		}
	});

	return page;
});

define('js/model/compare/ComparePlantConfigProxy',[
	'js/model/ConfigProxy',
	'js/model/mixins/ConfigMixinDownload',
    'js/model/mixins/ConfigMixinPreviousPage',
], function(
		ConfigProxy,
		ConfigMixinDownload,
        ConfigMixinPreviousPage
) {
	return ConfigProxy.$extend({
		__include__: [
			ConfigMixinDownload,
            ConfigMixinPreviousPage
		]
	});
});

define('js/model/compare/ComparePlantRequestProxy',[
	'js/model/RequestProxy'
], function(
		RequestProxy
) {
	return RequestProxy.$extend({
		__include__: [		],

		getPlants : function() {
			var data = this.getData();
			var plants = [];
			for(var i = 0; i < data.vals_.length; i = i + 2) {
				if(!isNaN(data[i])) {
					plants.push(data[i]);
				}
			}

			return plants.join('/');
		},

		getQuery : function() {
			return $.extend({}, this.$super(), {'plants' : this.getPlants()});
		},

		getTopicId : function() {
			return 1;
		}
	});
});

define('js/model/compare/ComparePlantParameterProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/RequestProxy',
	'js/model/ParameterProxy',
	'js/model/mixins/ParameterMixinFrequency',
	'js/model/mixins/ParameterMixinDateCode',
	'js/model/mixins/ParameterMixinTopic'
], function(
		Facade,
		ConfigProxy,
		RequestProxy,
		ParameterProxy,
		ParameterMixinFrequency,
		ParameterMixinDateCode,
		ParameterMixinTopic
) {
	return ParameterProxy.$extend({

		__include__: [
			ParameterMixinFrequency,
			ParameterMixinDateCode,
			ParameterMixinTopic
		],

		getQueryKeys: function() {
			return [ 'freq', 'datecode'];
		},

		getGAString: function(params, topicId) {
			var qry = $.extend({}, this.getQuery(), params);
			// TODO: GA String
			var ret = '/plant/compare/';

			return ret;
		},

		getGAStringByTopic: function(params, topicId) {
			params.datecode = null;
			return this.getGAString(params, topicId);
		},

		getFullPath: function(params) {
			var requestProxy = this.facade.retrieveProxy(RequestProxy.NAME);
			return 'plant/compare/' + (params.plants ? params.plants : this.getPlants()).split(',').join('/') +
					this.getQueryString(params);
		},

		getTableParams: function() {
			return {
				'plants': this.getPlants(),
				'freq': this.getFreq(),
				'datecode': this.getDatecode()
			};
		},

		getTableParamsString: function() {
			var ret = [],
					params = this.getTableParams();

			$.each(params, function(key, value){
				ret.push(key + '=' + value.toString());
			});

			return ret.join('&');
		},

		getTimeParams: function() {
			return {
				'datecode': this.getDatecode(),
				'freq': this.getFreq()
			};
		},

		getPlants : function() {
			return this.getQuery().plants.split('/').join(',');
		},

		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] + '&';
			}
			return q.substring(0, q.length - 1);
		},

		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(),
					timeParamsUpdated = !('datecode' in requestProxy.getQuery()),
					frequencyParamsUpdated = !('freq' in requestProxy.getQuery());

			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 |= timeParamsUpdated |= tmpKey == 'datecode';
				// Check and see if our frequency has changed
				tableParamsUpdated |= frequencyParamsUpdated |= tmpKey == 'freq';
				// Check and see if our plants have changed
				tableParamsUpdated |= tmpKey == 'plants';
			});

            if(timeParamsUpdated || frequencyParamsUpdated) {
                this.sendNotification(Facade.TIME_PARAMS_UPDATED);
            }
			if (frequencyParamsUpdated)
				this.sendNotification(Facade.FREQUENCY_PARAM_UPDATED);
			if (tableParamsUpdated)
				this.sendNotification(Facade.TABLE_PARAMS_UPDATED);
		},

		validateQuery: function(vals) {
			var configProxy = this.facade.retrieveProxy(ConfigProxy.NAME),
					topic = configProxy.getTopic(this.getTopicId());

			this.validateFrequency(vals, topic);
			this.validateDatecode(vals, topic);
		}

	});
});

define('js/model/compare/ComparePlantTableDataProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy'
], function(
		Facade,
		ConfigProxy,
		ParameterProxy,
		TableDataProxy
) {
	return TableDataProxy.$extend({

		isUpdating: false,

		__include__: [
		],

		getTableData: function() {
			return this.getData().DATA;
		},

		getConsumptionFuelTypes : function() {
			return this.getData().CONSUMPTION_ROWS;
		},

		getTableTitle: function() {
			return 'Powerplant comparison';
		},

		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.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) {
								self.setData(tableParamsString, data);
								self.isUpdating = false;
								self.sendNotification(Facade.TABLEDATA_UPDATED);
							}
						}
				);
			}
		},

        getTableDataStartEndDates: function() {
            var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
                configProxy = this.facade.retrieveProxy(ConfigProxy.NAME);
            return configProxy.getTopic(parameterProxy.getTopicId());
        },

		__classvars__: {
			PROXY_URL: 'data/index.php',
			PROXY_METHOD: 'getPlantCompare',
			CLEAR: 'clear'
		}
	});
});

define('js/model/compare/ComparePlantChartProxy',[
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/DataProxy'
], function(
		Facade,
		ConfigProxy,
		ParameterProxy,
		DataProxy
) {
	return DataProxy.$extend({

		isUpdating: false,

		__include__: [
		],

		getParamString : function(dataType) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					plants = parameterProxy.getPlants();

			return "datatype=" + dataType + "&plants=" + plants;
		},

		loadData: function(dataType) {
			var paramString = this.getParamString(dataType),
					self = this;

			this.isUpdating = true;

			if (this.inCache(paramString)) {
				this.setDataFromCache(paramString);
				this.isUpdating = false;
				this.sendNotification(Facade.COMPARE_CHART_DATA_UPDATED);
			} else {
				$.ajax(
						encodeURI(
								this.$class.PROXY_URL + '?method=' +
								this.$class.PROXY_METHOD + '&' + paramString
						), {
							dataType: 'json',
							error: function() {
								self.sendNotification(Facade.ERROR);
							},
							success: function(data) {
								self.setData(paramString, data);
								self.isUpdating = false;
								self.sendNotification(Facade.COMPARE_CHART_DATA_UPDATED);
							}
						}
				);
			}
		},

		__classvars__: {
			NAME : 'ChartDataProxy',
			PROXY_URL: 'data/index.php',
			PROXY_METHOD: 'getPlantCompareChart',
			CLEAR: 'clear'
		}
	});
});


define('text!templates/plantCompare.html',[],function () { return '<div id="top_controls">\r\n    <div id="back_button_container" class="back"></div>\r\n\t<div class="date-download">\r\n\t\t<div id="time-selection-controls"  class="time-selection-controls">\r\n\t\t\t<div id="frequency-selection-button-container" class="selection-menu-button frequency">\r\n\t\t\t\t{{>frequency_selection_button}}\r\n\t\t\t</div>\r\n\t\t\t<div id="time-period-selection-button-container" class="selection-menu-button frequency">\r\n\t\t\t\t{{>time_period_selection_button}}\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t\t{{#mainDownloadButtonOpts}} {{>download_button}} {{/mainDownloadButtonOpts}}\r\n\t</div>\r\n</div>\r\n<div class="clear"></div>\r\n<div id="compared_plants"></div>\r\n<div id="tableContainer"></div>\r\n<div class="clear"></div>\r\n<div id="footnote_container"></div>\r\n<div class="clear"></div>\r\n<div id="download_dialog_wrap">\r\n\t<div id="download_dialog_container">\r\n\t\t{{#overlayDownloadButtonOpts}} {{>download_button}} {{/overlayDownloadButtonOpts}}\r\n\t\t<div id="download_dialog"></div>\r\n\t</div>\r\n</div>\r\n<div id="frequency_dialog_container">\r\n\t<div id="frequency_dialog"></div>\r\n</div>\r\n<div id="time_dialog_container">\r\n\t<div id="time_dialog"></div>\r\n</div>';});

define('js/view/VC_ComparePageLayout',[
	'classy',
	// Templates
	'text!templates/plantCompare.html',
	'text!templates/download_button.html',
	'text!templates/frequency_selection_button.html',
	'text!templates/time_period_selection_button.html'
], function(
		Class,
		pageTemplate,
		downloadButtonTemplate,
		frequencySelectionButtonTemplate,
		timePeriodSelectionButtonTemplate
) {
	return Class.$extend({

		container: null,

		__init__: function(container, options) {
			this.container = container;

			this.container.empty().html(
					Mustache.render(pageTemplate, options, {
						'download_button': downloadButtonTemplate,
						'frequency_selection_button': frequencySelectionButtonTemplate,
						'time_period_selection_button': timePeriodSelectionButtonTemplate
					})
			);
		},
        
        getBackButtonContainer: function() {
            return $('#back_button_container', this.container);
        },

		getDownloadContainer: function() {
			return $('#download_dialog', this.container);
		},
		
		getFrequencyContainer: function() {
			return $('#frequency_dialog_container', this.container);
		},

		getTimePeriodContainer: function() {
			return $('#time_dialog_container', this.container);
		},

		getTableContainer: function() {
			return $('#tableContainer', this.container);
		},

		getFootnoteContainer: function() {
			return $('#footnote_container', this.container);
		},

	});
});


define('text!templates/plantCompareHeader.html',[],function () { return '{{#plants}}\r\n<div id="plant-{{id}}" class="plant-compare-header">\r\n\t<div class="remove"></div>\r\n\t<div class="clear"></div>\r\n\t<div class="label">\r\n\t\t{{name}}\r\n\t</div>\r\n</div>\r\n{{/plants}}';});


define('text!templates/plantCompareTable.html',[],function () { return '<table class="basic_table fullwidth all_left">\r\n\t<thead>\r\n\t\t<th colspan="2"></th>\r\n\t\t<th></th>\r\n\t\t{{#plants}}\r\n\t\t<th>\r\n\t\t\t<div data-plant_code="{{plant_code}}">\r\n\t\t\t\t<div class="remove leaflet-control"></div>\r\n\t\t\t\t<!--Leaflet map-->\r\n\t\t\t\t<div id="map-{{plant_code}}" class="map">\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class="label">\r\n\t\t\t\t\t{{plant_name}}\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</th>\r\n\t\t{{/plants}}\r\n\t</thead>\r\n\t<tbody>\r\n\t\t<tr>\r\n\t\t\t<td>Plant name</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{plant_name}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Plant code</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{plant_code}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Operator</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{utility_name}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Utility ID</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{utility_id}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>State</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{state}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Sector</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{sector_name}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Steam plant type</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{boiler_type}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t</tbody>\r\n\t<tbody>\r\n\t\t<tr>\r\n\t\t\t<td colspan="{{numCols}}">Generation</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Operable generators</td>\r\n\t\t\t<td><a class="ico linechart" data-type="operable_generators" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=operable_generators"></a></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n            <td>{{#formatText}}{{operable_generators}}{{/formatText}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Prime movers</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n            <td>{{#formatText}}{{prime_movers}}{{/formatText}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Fuel types</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n            <td>{{#formatText}}{{fuel_types}}{{/formatText}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Primary technologies</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n            <td>{{#formatText}}{{primary_technology_list}}{{/formatText}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Summer capacity</td>\r\n\t\t\t<td><div class="ico linechart" data-type="summer_capacity" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=summer_capacity"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{#formatNumber}}{{summer_capacity}}:1{{/formatNumber}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Gross generation</td>\r\n\t\t\t<td><div class="ico linechart" data-type="gross_generation" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=gross_generation"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{#formatNumber}}{{gross_generation}}:0{{/formatNumber}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Net generation</td>\r\n\t\t\t<td><div class="ico linechart" data-type="net_generation" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=net_generation"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n\t\t\t<td>{{#formatNumber}}{{net_generation}}:0{{/formatNumber}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n\t</tbody>\r\n\t<tbody>\r\n\t\t<tr class="header_row">\r\n\t\t\t<td colspan="{{numCols}}">Consumption</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Operable boilers</td>\r\n\t\t\t<td><div class="ico linechart" data-type="operable_boilers" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=operable_boilers"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#plants}}\r\n            <td>{{#formatText}}{{operable_boilers}}{{/formatText}}</td>\r\n\t\t\t{{/plants}}\r\n\t\t</tr>\r\n        {{#fuelTypes}}\r\n        <tr>\r\n            <td>{{#formatCase}}{{desc}}{{/formatCase}}</td>\r\n            <td><div class="ico linechart" data-type="{{desc}}" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=fuel_types&fuelCode={{agg_fuel_code}}"></div></td>\r\n\t\t\t<td></td>\r\n            {{#columns}}\r\n            <td>{{#formatNumber}}{{value}}:0{{/formatNumber}}</td>\r\n            {{/columns}}\r\n        </tr>\r\n        {{/fuelTypes}}\r\n\t</tbody>\r\n\t<tbody>\r\n\t\t<tr class="header_row">\r\n\t\t\t<td colspan="{{numCols}}">Water usage</td>\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Operable cooling systems</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n\t\t{{#waterUsage}}\r\n\t\t\t\t{{#operableCooling}}\r\n\t\t\t\t<td>{{value}}</td>\r\n\t\t\t\t{{/operableCooling}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Cooling system type</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n            {{#coolingSystem}}\r\n            <td>{{#formatText}}{{value}}{{/formatText}}</td>\r\n            {{/coolingSystem}}\r\n\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Water source name</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n            {{#waterSourceName}}\r\n            <td>{{#formatText}}{{value}}{{/formatText}}</td>\r\n            {{/waterSourceName}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Water discharge name</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n            {{#dischargeName}}\r\n            <td>{{#formatText}}{{value}}{{/formatText}}</td>\r\n            {{/dischargeName}}\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Water source</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n            {{#waterSource}}\r\n            <td>{{#formatText}}{{value}}{{/formatText}}</td>\r\n            {{/waterSource}}\r\n\r\n\t\t</tr>\r\n\t\t<tr>\r\n\t\t\t<td>Water type</td>\r\n\t\t\t<td></td>\r\n\t\t\t<td></td>\r\n            {{#waterType}}\r\n            <td>{{#formatText}}{{value}}{{/formatText}}</td>\r\n            {{/waterType}}\r\n\t\t</tr>\r\n\t\t{{/waterUsage}}\r\n\r\n\t\t{{#waterVolume.3}}\r\n\t\t<tr>\r\n\r\n\t\t\t<td>{{#formatCase}}{{WATER_DISCHARGE_TEMP_MAX}}{{/formatCase}}</td>\r\n\r\n\t\t\t<td><div class="ico linechart" data-type="{{WATER_DISCHARGE_TEMP_MAX}}" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=water_usage&fuelCode={{WATER_DISCHARGE_TEMP_MAX}}"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#volumeCols}}\r\n\t\t\t<td>{{#formatNumber}}{{value}}{{/formatNumber}}</td>\r\n\t\t\t{{/volumeCols}}\r\n\r\n\t\t</tr>\r\n\t\t{{/waterVolume.3}}\r\n\r\n\t\t{{#waterVolume.2}}\r\n\t\t<tr>\r\n\r\n\t\t\t<td>{{#formatCase}}{{WITHDRAWAL_VOLUME}}{{/formatCase}}</td>\r\n\r\n\t\t\t<td><div class="ico linechart" data-type="{{WITHDRAWAL_VOLUME}}" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=water_usage&fuelCode={{WITHDRAWAL_VOLUME}}"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#volumeCols}}\r\n\t\t\t<td>{{#formatNumber}}{{value}}{{/formatNumber}}</td>\r\n\t\t\t{{/volumeCols}}\r\n\r\n\t\t</tr>\r\n\t\t{{/waterVolume.2}}\r\n\r\n\t\t{{#waterVolume.0}}\r\n\t\t<tr>\r\n\r\n\t\t\t<td>{{#formatCase}}{{DISCHARGE_VOLUME}}{{/formatCase}}</td>\r\n\r\n\t\t\t<td><div class="ico linechart" data-type="{{DISCHARGE_VOLUME}}" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=water_usage&fuelCode={{DISCHARGE_VOLUME}}"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#volumeCols}}\r\n\t\t\t<td>{{#formatNumber}}{{value}}{{/formatNumber}}</td>\r\n\t\t\t{{/volumeCols}}\r\n\r\n\t\t</tr>\r\n\t\t{{/waterVolume.0}}\r\n\r\n\t\t{{#waterVolume.1}}\r\n\t\t<tr>\r\n\r\n\t\t\t<td>{{#formatCase}}{{CONSUMPTION_VOLUME}}{{/formatCase}}</td>\r\n\r\n\t\t\t<td><div class="ico linechart" data-type="{{CONSUMPTION_VOLUME}}" href="data/index.php?method=getPlantCompareChart&plants={{plantCodes}}&freq={{freq}}&dataType=water_usage&fuelCode={{CONSUMPTION_VOLUME}}"></div></td>\r\n\t\t\t<td></td>\r\n\t\t\t{{#volumeCols}}\r\n\t\t\t<td>{{#formatNumber}}{{value}}{{/formatNumber}}</td>\r\n\t\t\t{{/volumeCols}}\r\n\r\n\t\t</tr>\r\n\t\t{{/waterVolume.1}}\r\n\r\n\r\n\t</tbody>\r\n</table>';});

/**
 * Created by SGE on 7/25/2017.
 */
define('js/view/VC_CompareTable',[
			'classy',
			'signals',
			'leaflet',
			'esri-leaflet',
			'text!templates/plantCompareHeader.html',
			'text!templates/plantCompareTable.html'
		],
		function(Class, signals, leaflet, esriLeaflet, headerTemplate, tableTemplate) {
			function removePlantHandler(event) {
				var plantCode = $(event.target).parent().data('plant_code');

				event.data.viewComponent.removePlant.dispatch(plantCode);
			}
			
			function showChartHandler(event) {
				var dataType = $(event.target).data("type");

				event.data.viewComponent.showChart.dispatch(dataType);
			}
			
			return Class.$extend({

				removePlant : null,
				showChart : null,
				data : null,

				__init__: function(container) {
					this.container = container;

					this.removePlant = new signals.Signal();
					this.showChart = new signals.Signal();

					$(this.container).on('click', '.remove', { viewComponent : this }, removePlantHandler);
					$(this.container).on('click', '.ico.linechart', { viewComponent : this }, showChartHandler);


				},

				updateTable: function(data, freq) {
					this.data = data;
                    var plantCode = [];


					for(var i = 0; i < data.plants.length; i ++){
						plantCode.push(data.plants[i].plant_code);

                    }
					this.container.html(Mustache.render(tableTemplate, {
						freq : freq,
						plants: data.plants,
                        fuelTypes: data.fuelTypes,
						waterUsage: data.waterUsage,
						waterVolume: data.waterVolume,
                        plantCodes: plantCode,
						numCols : function() {
							return this.plants.length + 3;
						},
						formatNumber : function() {
							return function(text, render) {
								var number = text.split(':')[0],
										precision = text.split(':')[1];

								return render((isNaN(parseFloat(number)) ? '--' :
												Highcharts.numberFormat(
														number,
														precision,
														void 0,
														','
												)
								));
							};
						},
                        formatCase : function() {
                            return function(text) {
                                return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase().replace(/_/g,' ');
                            };
                        },
                        formatText : function() {
                            return function(text) {
                                return text === undefined || text === null || text === '' ? '--' : text;
                            };
                        }
					}));
					for(i = 0; i < data.plants.length; i ++) {
						var map = leaflet.map('map-' + data.plants[i].plant_code, {
							center: [parseFloat(data.plants[i].latitude), parseFloat(data.plants[i].longitude)],
							zoom: 14,
							zoomControl: false,
							dragging: false,
							boxZoom: false,
							doubleClickZoom: false,
							scrollWheelZoom: false,
							keyboard: false
						});
						esriLeaflet.basemapLayer("Imagery").addTo(map);
					}
				},

                outputToArrOfArrs : function(filtered){
                    filtered = filtered !== void 0 ? filtered : true;
                    var plants = this.data.plants,
                        data = this.data,
                        ret = [], row;
                    ret.push(row = []);

                    var formatCase = function(text) {
                        return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase().replace(/_/g,' ');
                    };

                    var generateRowData = function(dataList,label,columnDef) {
                        row = [];
                        row.push(formatCase(label));
                        $.each(dataList, function( i, col ) {
                            row.push(col[columnDef] === undefined || col[columnDef] === null ? '--' : col[columnDef]);
                        });
                        ret.push(row);
                    };

                    // Plant Data
                    var plantConfig = [
                        {'Plant name':'plant_name'},
                        {'Plant code':'plant_code'},
                        {'Operator':'operable_generators'},
                        {'Utility ID':'utility_id'},
                        {'State':'state'},
                        {'Sector':'sector_name'},
                        {'Steam plant type':'boiler_type'}
                    ];
                    $.each(plantConfig, function () {
                        $.each(this, function (label, dataColumn) {
                            generateRowData(plants,label,dataColumn);
                        });
                    });

                    // Generation Data
                    ret.push(row = []);
                    ret.push(row = ['Generation']);
                    var generationConfig = [
                        {'Operable generator':'operable_generators'},
                        {'Prime movers':'prime_movers'},
                        {'Fuel types':'fuel_types'},
                        {'Primary technologies':'primary_technology_list'},
                        {'Summer capacity':''}, // todo: make data driven
                        {'Gross generation':'gross_generation'},
                        {'Net generation':'net_generation'}
                    ];
                    $.each(generationConfig, function () {
                        $.each(this, function (label, dataColumn) {
                            generateRowData(plants,label,dataColumn);
                        });
                    });

                    // Consumption Data
                    ret.push(row = []);
                    ret.push(row = ['Consumption']);
                    ret.push(row = ['Operable boilers']); // todo: make data driven
                    $.each(this.data['fuelTypes'], function(idx, fuelType) {
                        generateRowData(fuelType['columns'],fuelType['desc'],'value');
                    });

                    // Water usage Data
                    ret.push(row = []);
                    ret.push(row = ['Water usage']);
                    var waterUsageConfig = [
                        {'Operable cooling systems':'operableCooling'},
                        {'Cooling system type':'coolingSystem'},
                        {'Water source name':'waterSourceName'},
                        {'Water discharge name':'dischargeName'},
                        {'Water source':'waterSource'},
                        {'Water type':'waterType'}
                    ];
                    $.each(waterUsageConfig, function () {
                        $.each(this, function (label, dataColumn) {
                            generateRowData(data['waterUsage'][0][dataColumn],label,'value');
                        });
                    });

                    // Water volume Data
                    if(data['waterVolume'].length > 0) {
                        var waterVolumeConfig = [
                            {'Water discharge temp max':'WATER_DISCHARGE_TEMP_MAX'},
                            {'Withdrawal volume':'WITHDRAWAL_VOLUME'},
                            {'Discharge volume':'DISCHARGE_VOLUME'},
                            {'Consumption volume':'CONSUMPTION_VOLUME'}
                        ];
                        $.each(waterVolumeConfig, function () {
                            $.each(this, function (label, dataColumn) {
                                dataColumn = data['waterVolume'].find( function( ele ) {
                                    return ele[dataColumn] != null;
                                });
                                generateRowData(dataColumn['volumeCols'],label,'value');
                            });
                        });
                    }

                    return ret;
                },
				
				destroy : function() {
					$(this.container).off('click', '.remove', removePlantHandler);
					$(this.container).off('click', '.ico.linechart', showChartHandler);
				}
			});
		});

/**
 * Created by SGE on 7/25/2017.
 */
define('js/view/MediatorCompareTable',[
	'puremvc',
	'js/Facade',
	'js/model/ConfigProxy',
	'js/model/ParameterProxy',
	'js/model/TableDataProxy'
], function(
		puremvc,
		Facade,
		ConfigProxy,
		ParameterProxy,
		TableDataProxy
) {
	return puremvc.Mediator.$extend({

		__init__: function(viewComponent) {
			this.$super(this.$class.NAME, viewComponent);
		},

		onRegister: function() {
			var self = this;

			this.getViewComponent().removePlant.add(function(plantCode) {
				console.log('remove : ' + plantCode);
				self.sendNotification(Facade.REMOVE_PLANT_COMPARE, {
					'plant_code': plantCode
				});
			});

			this.getViewComponent().showChart.add(function(dataType) {
				console.log('showChart : ' + dataType);
				compareDataType = dataType;
				self.sendNotification(Facade.SHOW_COMPARE_CHART, {
					'dataType': dataType.replace(' ','_')
				});
			});

			var chartData = {};
			var chart;
			var compareDataType;
			var units;
			var type;
			var waterDesc;
			var frequency;

            $('.ico.linechart').fancybox({
                'autoDimensions': false,
                'autoScale': false,
                'width': 900,
                'height': 400,
                'scrolling': 'auto',
                'type': 'ajax',
                'autoSize': false,
								'afterLoad' : function(event) {
									chartData = JSON.parse(event.content);
									event.content = '<div id="compareChartContainer"></div>';
								},
								'afterShow' : function(event) {
									chart = new Highcharts.Chart({
										chart : {
											type : 'line',
											renderTo : 'compareChartContainer'
										},
										series : seriesChartData(chartData),
										title : {
											text : chartTitle(compareDataType)
										},
										tooltip : {
											formatter: function () {
												var string;
												switch (frequency){
													case 'A':
														string = Highcharts.dateFormat('Annual: %Y', new Date(this.x)) + '<br>';
														break;
													case 'M':
														string = Highcharts.dateFormat('Month: %b %Y', new Date(this.x)) + '<br>';
														break;
													case 'Q':
														string =  Highcharts.dateFormat('Quarter: Q' + quarter (Highcharts.dateFormat('%m',new Date(this.x))) + ' %Y', new Date(this.x)) + '<br>';
														break;
												}
												return '<b>' + this.series.name + '</b> <br>' + string + Highcharts.numberFormat(this.y,0,',',',') + ' ' + yAxisTitle(type);
											}
										},
										xAxis : {
											type : 'datetime'
										},
										yAxis: {
											title : {
												text : yAxisTitle(type),
												align : 'above'
											}
										}
									});
								}

            });

           function seriesChartData (chartData) {
           		var seriesChart = [];
			  	$.each(chartData, function (i, plant) {
					seriesChart.push( {
						name: plant.plantName,
						data: plant.data
                  });
					units = plant.units;
					type = plant.dataType;
					waterDesc = plant.waterDesc;
					frequency = plant.frequency;
				});
			  	return seriesChart;
           }

			function chartTitle (string) {
				title = string.charAt(0).toUpperCase() + string.slice(1).toLowerCase().replace(/_/g,' ');
				if( type == 'fuel_types'){
					title += ' consumption';
				}
				return title;
			}

			function quarter (month){
				var quarter;
				switch (month){
					case '01':
						quarter = '1';
						break;
					case '04':
						quarter = '2';
						break;
					case '07':
						quarter = '3';
						break;
					case '10':
						quarter = '4';
						break;
				}
				return quarter;
			}

			function yAxisTitle (string){
				var yTitle = '';
				switch(string){
					case 'operable_generators':
						yTitle = 'number of generators';
						break;
					case 'summer_capacity':
						yTitle = 'megawatts';
						break;
					case 'gross_generation':
					case 'net_generation':
						yTitle = 'megawatthours';
						break;
					case 'fuel_types':
						yTitle = units;
						if (!units){
							yTitle = 'mmBtu';
						}
						break;
					case 'water_usage':
						switch (waterDesc){
							case 'WATER_DISCHARGE_TEMP_MAX':
								yTitle = 'degrees fahrenheit';
								break;
							case 'WITHDRAWAL_VOLUME':
							case 'DISCHARGE_VOLUME':
							case 'CONSUMPTION_VOLUME':
								yTitle = 'million gallons';
								break;
						}
						break;
				}
				return yTitle;
			}

		},

		listNotificationInterests: function() {
			return this.$super().concat([
				Facade.TABLEDATA_UPDATED,
				Facade.DOWNLOAD_OPTION_SELECTED
			]);
		},

		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(),
							freq = parameterProxy.getFreq();

					vc.updateTable(data,freq);

					this.sendNotification(Facade.TABLE_RENDERED);
					break;
				case Facade.DOWNLOAD_OPTION_SELECTED:
					if (note.body.target == 'table' && note.body.type == 'csv') {
						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;
				default:
					this.$super(note);
					break;
			}
		},

		onRemove : function() {
			this.getViewComponent().destroy();
		},

		__classvars__: {

			NAME: 'CompareTableMediator'
		}
	});
});


define('text!templates/back_button.html',[],function () { return '<a id="back_button" href="./#/{{url}}">< {{buttonText}}</a>';});

define('js/pages/PlantComparePage',[
	// Modules
	'hasher',
	'signals',
	'puremvc',
	'classy',
	'page',
	'js/Facade',
	'js/Controller',
		//model
	'js/model/compare/ComparePlantConfigProxy',
	'js/model/compare/ComparePlantRequestProxy',
	'js/model/compare/ComparePlantParameterProxy',
	'js/model/compare/ComparePlantTableDataProxy',
	'js/model/HelpTooltipProxy',
	'js/model/compare/ComparePlantChartProxy',
		// view components
	'js/view/VC_ComparePageLayout',
	'js/view/VC_CompareTable',
    'js/view/VC_Download',
	'js/view/VC_Frequency_Selection_Button',
	'js/view/VC_Time_Period_Selection_Button',
		// mediators
	'js/view/MediatorCompareTable',
    'js/view/MediatorDownload',
	'js/view/MediatorFrequencySelectionButton',
	'js/view/MediatorTimePeriodSelectionButton',
        // templates
    'text!templates/back_button.html'
], function(
		//Modules
		hasher,
		signals,
		puremvc,
		Class,
		Page,
		Facade,
		Controller,
		//model
		ConfigProxy,
		RequestProxy,
		ParameterProxy,
		TableDataProxy,
		HelpTooltipProxy,
		ChartDataProxy,
		// view components
		PageLayout,
		CompareTable,
        DownloadDialog,
		FrequencySelectionButtonDialog,
		TimePeriodSelectionButtonDialog,
		//mediators
		MedaiatorCompareTable,
        DownloadMediator,
		FrequencySelectionButtonMediator,
		TimePeriodSelectionButtonMediator,
        //templates
        BackButtonTemplate

) {
	var facade = null;

	///////////////
	// Shortcuts //
	///////////////


	//////////
	// Page //
	//////////

	var page = Page.$extend({
		load: function(request) {
			$('#pagecontent').addClass('plantCompare');
			this.$super(request);
			Page.clearErrorMessage();
			if (!facade) {
				facade = PlantCompareFacade.getInstance(this.name);
				facade.sendNotification(PlantCompareFacade.STARTUP, request);
			} else
				facade.sendNotification(PlantCompareFacade.RELOAD, request);
		},
		unload: function() {
			facade.sendNotification(PlantCompareFacade.UNLOAD);
			PlantCompareFacade.removeCore(this.name);
			facade = null;
			this.$super();
			$('#pagecontent').removeClass('plantCompare');
		}
	})('js/pages/PlantComparePage');

	////////////
	// Facade //
	////////////

	var PlantCompareFacade = 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.REMOVE_PLANT_COMPARE, RemovePlantCommand);
			this.registerCommand(this.$class.SHOW_COMPARE_CHART, FetchChartDataCommand);
			this.registerCommand(this.$class.TIME_SELECTION_UPDATED, Controller.UpdateHashCommand);
			this.registerCommand(this.$class.FREQUENCY_SELECTION_UPDATED, Controller.FrequencySelectionUpdatedSingleTimeSliderCommand);
            this.registerCommand(this.$class.CSV_DATA_GENERATED, Controller.OutputToCSVCommand);
			this.registerCommand(this.$class.UNLOAD, ShutdownCommand);
		},

		__classvars__: {

			getInstance: function(key) {
				if (!puremvc.Facade.hasCore(key)) {
					new PlantCompareFacade(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 ShutdownCommand = puremvc.MacroCommand.$extend({
		initializeMacroCommand: function() {
			this.addSubCommand(RemoveViewCommand);
			this.addSubCommand(RemoveModelCommand);
		}
	});

	var RemoveViewCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeMediator(PageMediator.NAME);
		}
	});

	var RemoveModelCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			this.facade.removeProxy(ConfigProxy.NAME);
			this.facade.removeProxy(RequestProxy.NAME);
			this.facade.removeProxy(ParameterProxy.NAME);
			this.facade.removeProxy(TableDataProxy.NAME);
			this.facade.removeProxy(HelpTooltipProxy.NAME);
			this.facade.removeProxy(ChartDataProxy.NAME);
		}
	});

	var InitializeModelCommand = puremvc.SimpleCommand.$extend({
		execute: function(note) {

			var self = this;

			var configProxy = new ConfigProxy();
			var requestProxy = new RequestProxy(note.getBody());
			var parameterProxy = new ParameterProxy();
			var tableDataProxy = new TableDataProxy();
			var helpTooltipProxy = new HelpTooltipProxy();
           	var chartDataProxy = new ChartDataProxy();

			this.facade.registerProxy(requestProxy);
			this.facade.registerProxy(configProxy);
			this.facade.registerProxy(parameterProxy);
			this.facade.registerProxy(tableDataProxy);
			this.facade.registerProxy(helpTooltipProxy);
            this.facade.registerProxy(chartDataProxy);
            
            var downloadParams = $.extend([], [{
                label: 'Data',
                children: [{
                    id: 'csv_table',
                    type: 'csv',
                    'class': 'ico_csv',
                    target: 'table',
                    label: 'Table (CSV)'
                }]
            }
            ]);
			configProxy.setDownloadParameters(downloadParams);
		}
	});

	var InitializeViewCommand = puremvc.SimpleCommand.$extend({
		execute: function() {
			var pageLayout = new PageLayout();
			var pageMediator = new PageMediator(pageLayout);
			this.facade.registerMediator(pageMediator);
		}
	});

	var RemovePlantCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			var parameterProxy = this.facade.retrieveProxy(ParameterProxy.NAME),
					currentPlants = parameterProxy.getPlants().split(','),
					newPlants = [];
			
			for(var i = 0; i < currentPlants.length; i++) {
				
				if(currentPlants[i].toString() !== note.getBody().plant_code.toString()) {
					newPlants.push(currentPlants[i].toString());
				}
			}

			Page.setHash(parameterProxy.getFullPath({plants : newPlants.join(',')}));
		}
	});
	
	var FetchChartDataCommand = puremvc.SimpleCommand.$extend({
		execute : function(note) {
			var chartDataProxy = this.facade.retrieveProxy(ChartDataProxy.NAME);

			chartDataProxy.loadData(note.getBody().dataType);
		}
	});

	///////////////
	// 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),
                requestProxy = this.facade.retrieveProxy(RequestProxy.NAME),
                topic = configProxy.getTopic(1),
                parameterProxy = facade.retrieveProxy(ParameterProxy.NAME);

			var compareTable = new CompareTable(pageLayout.getTableContainer());
			var frequencySelectionButtonDialog = new FrequencySelectionButtonDialog(pageLayout.getFrequencyContainer());
			var timePeriodSelectionButtonDialog = new TimePeriodSelectionButtonDialog(pageLayout.getTimePeriodContainer(), topic);
			var downloadDialog = new DownloadDialog(pageLayout.getDownloadContainer());

			var compareTableMediator = new MedaiatorCompareTable(compareTable);
			var frequencySelectionButtonMediator = new FrequencySelectionButtonMediator(frequencySelectionButtonDialog);
			var timePeriodSelectionButtonMediator = new TimePeriodSelectionButtonMediator(timePeriodSelectionButtonDialog);
			var downloadMediator = new DownloadMediator(downloadDialog);

			this.facade.registerMediator(frequencySelectionButtonMediator);
			this.facade.registerMediator(timePeriodSelectionButtonMediator);
			this.facade.registerMediator(compareTableMediator);
            this.facade.registerMediator(downloadMediator);

            // Back button
            var prevPage = configProxy.getPreviousPage();
            var url = (prevPage ? prevPage.request_ : parameterProxy.getFullTopicPath(requestProxy.getQuery(), requestProxy.getTopicId()));
            pageLayout.getBackButtonContainer().html(Mustache.to_html(BackButtonTemplate, { url: url, buttonText: 'Back to plant list' }));
        },
		listNotificationInterests: function() {
			return this.$super().concat([

			]);
		},

		handleNotification: function(note) {

		},

		onRemove : function() {

			this.facade.removeMediator(MedaiatorCompareTable.NAME);
			this.facade.removeMediator(DownloadMediator.NAME);
			this.facade.removeMediator(FrequencySelectionButtonMediator.NAME);
			this.facade.removeMediator(TimePeriodSelectionButtonMediator.NAME);
		},

		__classvars__: {
			NAME: 'PageMediator'
		}

	});


	/////////////////////
	// View Components //
	/////////////////////

	PageLayout = PageLayout.$extend({

		__init__: function() {
			this.$super($('#pagecontent'), {
				'mainDownloadButtonOpts': {
					'selected': false,
					'id': 'main_download_button'
				},
				'overlayDownloadButtonOpts': {
					'selected': true,
					'id': 'overlay_download_button'
				},
				'timePeriodSelectionButtonOpts': {
					'selected': false
				},
				'frequencySelectionButtonOpts': {
					'selected': false
				}
			});
        }
	});

	return page;
});

define('js/routes',[
	'js/pages/AggregateDataPage',
	'js/pages/PlantListPage',
	'js/pages/PlantDataPage',
	'js/pages/PlantComparePage'
], {
	'defaultHash': 'topic/0',
	'patterns': [{
		'pattern': /topic\/(\d{1,3})\/?(\?.*)?/,
		'moduleURI': 'js/pages/AggregateDataPage',
		'rules': {
			0: [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 56, 57]
		}
	}, {
		'pattern': /topic\/(\d{1,3})\/?(\?.*)?/,
		'moduleURI': 'js/pages/PlantListPage',
		'rules': {
			0: [1, 101]
		}
	}, {
		'pattern': /plant\/()(\d+)\/?(\?.*)?/,
		'moduleURI': 'js/pages/PlantDataPage'
	}, {
		'pattern': /plant\/compare\/(\d{1,5})(\/(\d{1,5}))?(\/(\d{1,5}))?(\/(\d{1,5}))?\/?(\?.*)?$/,
		'moduleURI': 'js/pages/PlantComparePage'
	}]
});


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 = pattern.rules, key, oldRule;

			function oldRuleWrapper(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;
				};
			}

			if (rules) {
				for (key in rules) {
					if (rules.hasOwnProperty(key) && key != 'normalize_') {
						oldRule = rules[key];
						rules[key] = oldRuleWrapper(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) {   //testing error handling on vm-server
						if (location.host.indexOf("wwwdev") != -1 || location.host.indexOf("vdc") != -1) {
							throw e;
						} else {
							var jsonData = JSON.stringify(e);
							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) {   //testing error handling on 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(){});