
//the map object
var map = null;
var mapClickHandler = null;
var polygonLocations = null;
var drawing = false;
var polygon = null;
var oracle = false;
//list of all topics
var topicGroups = null;

var globalLayerIndex = 1;

var colorRamps = new Array();

//tile generic settings
var layerOpacity = 0.8;
var currentSelectedItem = null;
var isSettingsPanelVisible = false;
var isTopicsPanelVisible = false;
var settingsTabPanel = null;

/******************************
 * Class declaration
 ******************************/        
var TopicGroup = new Class ({
    prebind: ['topicClicked', 'deactivate', 'tabSelected', 'onTopicChanged'],
    
    initialize: function(id, image, activatedImage, caption, description ) {
        this.id = id;
        this.image = image;
        this.activatedImage = activatedImage;
        this.caption = caption;
        this.description = description;
    },
    
    //Members
    activatedImage: '',
    caption: '',
    description: '', 
    id: '',
    image: '',
    isActivated: false,
    topics: null,
    
    
    //Methods
    activate: function() {
        this.isActivated = true;
        $(this.id).parent().setClass('topicButtonSelected');
        this.showDeactivateButton();
        this.showTopicSettings();
        
        this.topics.each(function(topic) {
            if(topic.isActivated) {
                if(!topic.forceLayerIndex)
                    topic.layerIndex = globalLayerIndex++;
                else
                    topic.layerIndex = 99999999;
            }
                
        });
        
        var currentGroup = this;
        this.topics.each(function(topic) {
            if(topic.forceLayerIndex)
                topic.layerIndex = currentGroup.getSmallestIndex();
        });
        
        loadTileFromTopics();
    },
    
    addTopic: function(topic) {
        if(!this.topics)
            this.topics = new Array();
        
        this.topics.push(topic);  
    },
    

    
    appendSettingsHtmlElement: function(element) {
        
        element.clean();

        var header = new Element('div', {
            id: 'settingsHeader'
        });
        element.append(header);
    
        
        //append the topic dropdown
        var topicDropDown = new Element('select', {
            id:'cmb_' + this.id,
            'class': 'dropDownTopic',
            on: {
                change: this.onTopicChanged
            }
        });
        
        var index = 0;
        this.topics.each(function(topic) {
            topic.indexInDropDown = index++;
            topicDropDown.append(new Element('option', {
                value: topic.id
            }).append(index + '. ' + topic.name));
            
        });
        
        topicDropDown.set('value', this.id);
        
        header.append(topicDropDown);
        
        //append the info button
        var topicGroup = this;
        var infoBtn = new Element('div', {
            'class': 'infoButton',
            on: {
                    click: function() {
                        var content = new Element('span');
                        content.append(new Element('img', {
                            src: topicGroup.image,
                            height: '32',
                            width: '32',
                            style: 'display:block;margin-bottom:4px;'
                        }));
                        content.append(topicGroup.description);
                        Lightbox.show(content).resize({width:'350px'});
                    }
            },
            style: 'float:right;'
        });
        
        infoBtn.append(new Element('img', {src: 'img/info.png'}));
        header.append(infoBtn);

        //Append the Drop Down
        var tabPanel = new Element('ul',{
            'class': 'topicSettingsTab',
            id: 'topicSettingsTab'
        });
        
        element.append(tabPanel);
        
        settingsTabPanel = new Tabs('topicSettingsTab', {scrollTabs: true});
        //settingsTabPanel.on('remove', this.tabRemoved);
        settingsTabPanel.on('select', this.tabSelected);
        
        this.topics.each(function(topic) {
            if(topic.isActivated) {
                var content = topic.getHtmlSettings();
                var tab = settingsTabPanel.add(topic.indexInDropDown+1, content, {id: 'tab-' + topic.id});
                topic.indexInTab = tab.tabs.length -1;
                settingsTabPanel.select(topic.indexInTab);
            }
        });
        
        
        if(settingsTabPanel.tabs.length > 1)
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'visible');});
        else
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'hidden');});
        
        return element;
    },
    
    deactivate: function() {
        this.isActivated = false;
        $(this.id).parent().setClass('topicButton');
        this.hideSettingsButton();
        clearSettings();
        loadTileFromTopics();  
    },
    
    getHtmlElement: function() {
        var topicDiv = new Element('div', {
            'class': 'topicCell'
        });
        
        
        var topicButton = new Element('div', {
            'class': 'topicButton'
        });
        
        var topicImage = new Element('img', {
            id: this.id,
            src: this.image,
            alt: this.caption,
            height: 32,
            width: 32,
            on: {
                 click: this.topicClicked
            }
        });
        
        topicButton.append(topicImage)
        topicDiv.append(topicButton);
        
        return topicDiv;
    },
    
    getSmallestIndex: function() {
        var smallestIndex = 99999999;
        this.topics.each(function(topic){
            if(topic.isActivated && topic.layerIndex < smallestIndex)
                smallestIndex = topic.layerIndex;
        });
        
        if(smallestIndex == 99999999)
            smallestIndex = globalLayerIndex;
            
        return smallestIndex;          
    },
    hideSettingsButton: function() {
        $(this.id + '_s').parent().remove();
    },
    
    
    onTopicChanged: function() {
        var elementId = 'cmb_' + this.id;
        var dropDown = $(elementId);
        if(!dropDown)
            return;
        
        var selectedValue = dropDown.get('value').toInt();
        
        //check if the value isn't already selected
        var selectedTopic = this.topics.first(function(topic) {return topic.id == selectedValue});
        
        if(selectedTopic) {
            if(selectedTopic.isActivated) {
                settingsTabPanel.select(selectedTopic.indexInTab);
            }
            else {
                selectedTopic.activate();
                loadTileFromTopics();
            }
        }
        
        if(settingsTabPanel.tabs.length > 1)
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'visible');});
        else
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'hidden');});
        
    },
    
    showTopicSettings: function() {
        
        var activatedCount = 0;
        this.topics.each(function(topic) {
            if(topic.isActivated)
                activatedCount = activatedCount + 1;
        });

        this.appendSettingsHtmlElement($('settings'));

        if(activatedCount == 0)
            this.topics[0].activate();

        showSettings();
    },
    
    showDeactivateButton: function() {
        
        var div = new Element('div', {
            'class': 'topicDesactivateButton'    
        })
        
        var setting = new Element('img', {
            id: this.id + '_s',
            src: 'img/round_delete_20.png',
            alt: this.caption,
            height: 32,
            width: 32,
            on: {
                 click: this.deactivate
            }
        });
        
        div.append(setting);
        
        $(this.id).parent().parent().insert(div);
    },
    
    tabSelected: function(tab) {
        var id = tab.target.id.replace('tab-', '').toInt();
        var topic = this.topics.first(function(t) {return t.id == id});
        if(topic) {
            var dropDownId = 'cmb_' + topic.group.id;
            if($(dropDownId)) {
                $(dropDownId).set('value', topic.id);
            }
        }
    },
    topicClicked: function() {
        if(!this.isActivated)
            this.activate();
        else
            this.showTopicSettings();
    }
    
});

/****************************
 * Topic Class
 ***************************/
var Topic = new Class({
    prebind: ['addNewRange', 'hideTopic'], 
    
    //CTOR
    initialize: function(params) {
        for(var param in params) {
            switch(param)
            {
                case 'id':
                    this.id = params[param];
                    break;
                case 'name':
                    this.name = params[param];
                    break;
                case 'defaultMin':
                    this.defaultMin = params[param];
                    break;
                case 'defaultMax':
                    this.defaultMax = params[param];
                    break;
                case 'defaultIncrement':
                    this.defaultIncrement = params[param];
                    break;
                case 'from':
                    this.from = params[param];
                    break;
                case 'selectStatement':
                    this.selectStatement = params[param];
                    break;
                case 'whereClause':
                    this.whereClause = params[param];
                    break;
                case 'mode':
                    this.mode = params[param];
                    break;
                case 'icons':
                    this.icons = params[param];
                    break;
                case 'breaks':
                    this.breaks = params[param];
                    break;
                case 'border':
                    this.border = params[param];
                    break;
                case 'colors':
                    this.colors = params[param];
                    break;
                case 'group':
                    this.group = params[param];
                    break;
                case 'supportRanges':
                    this.supportRanges = params[param];
                    break;
                case 'unitOfMeasure':
                    this.unitOfMeasure = params[param];
                    break;
                case 'handleAsPercentage':
                    this.handleAsPercentage = params[param];
                    break;
                case 'whereClauseTemplate':
                    this.whereClauseTemplate = params[param];
                    break;
                case 'colorSuffix':
                    this.colorSuffix = params[param];
                    break;
                case 'colorPadding':
                    this.colorPadding = params[param];
                    break;
                case 'forceLayerIndex':
                    this.forceLayerIndex = params[param];
                    break;
                case 'layerIndex':
                    this.layerIndex = params[param];
                    break;
            }
        }
    },
    
    //Members
    border: '',
    breaks: '',
    colors: '',
    colorSuffix: '',
    colorPadding: '',
    defaultIncrement: 0,
    defaultMin: 0,
    defaultMax: 0,
    forceLayerIndex: false,
    from: '',
    group: null,
    handleAsPercentage: false,
    layerIndex: 0,
    icons: '',
    id: '',
    indexInDropDown: 0,
    indexInTab: 0,
    isActivated: false,
    mode: '',
    name: '',
    ranges: null,
    selectStatement: '',
    supportRanges: true,
    unitOfMeasure: '',
    whereClause: '',
    whereClauseTemplate: '',
    
    //Methods
    activate: function() {
        
        if(!this.forceLayerIndex) {
            this.layerIndex = globalLayerIndex++;
        }
        else {
            this.layerIndex = this.group.getSmallestIndex();
        }
        this.isActivated = true;
        this.ranges = new Array();
        this.addRange(this.defaultMin, this.defaultMax, 1, 0, 0);
        var content = this.getHtmlSettings();
        var tab = settingsTabPanel.add(this.indexInDropDown+1, content, {id: 'tab-' + this.id});
        this.indexInTab = tab.tabs.length -1;
        settingsTabPanel.select(this.indexInTab);
    },
    
    addRange: function(minValue, maxValue, elementId, colorIndex, rangeIndex) {
        this.ranges.push(new TopicRange({
                min: minValue,
                max: maxValue,
                id: elementId,
                rangeIndex: rangeIndex,
                colorIndex: colorIndex,
                topic: this
            }));
        this.computeBreaks();
    },
    
    addNewRange: function() {
        
        
        var lastRange = this.ranges.last();
        var min = 0;
        var max = 0;
        
        if(lastRange) {
            min = lastRange.max;
            max = min + this.defaultIncrement;
            this.addRange(min, max, lastRange.id + 1, lastRange.colorIndex, lastRange.rangeIndex + 1);
        }
        else {
            this.addRange(this.defaultMin, this.defaultMax, 1, 0, 0);
        }
        
        
        this.ranges.last().appendHtml($('rangeList'+this.id));
        loadTileFromTopics();
        
        if(this.ranges.length >= 5) {
            var elementId = 'addRange' + this.id;
            $(elementId).hide();
        }
    },
    
    computeBreaks: function() {
        
        if(!this.supportRanges)
            return;
        
        var breaks = '';
        var colors = '';
        var min= 999999999999;
        var max= -999999999999;
        var lastMax = max;
        
        this.ranges.each(function(range) {
            if(range.getMin() != lastMax) {
                if(breaks.length > 0)
                    breaks = breaks + ',' + range.getMin();
                else
                    breaks = '' + range.getMin();
                
            }
            breaks = breaks + ',' + range.getMax();
            lastMax = range.getMax();
            
            if(range.getMin() < min)
                min = range.getMin();
            
            if(range.getMax() > max)
                max = range.getMax();
           
            if(colors.length > 0) 
                colors = colors + ',' + colorRamps[range.colorIndex][range.rangeIndex] + range.topic.colorSuffix;
            else
                colors = colorRamps[range.colorIndex][range.rangeIndex] + range.topic.colorSuffix;
        });
        
        this.whereClause = this.whereClauseTemplate.replace('{@min}', min).replace('{@max}', max);
        this.breaks = breaks;
        this.colors = colors;//'0000' + this.colorSuffix + colors + ',0000' + this.colorSuffix;
        
        if(this.colorPadding.length > 0)
            this.colors = this.colorPadding + ',' + this.colors + ',' + this.colorPadding;
        
    },
    
    deleteRange: function(range) {
        var index = this.ranges.indexOf(range);
        this.ranges.splice(index, 1);
        
        var i=0;
        this.ranges.each(function(current) {
            current.rangeIndex = i++;
        });
        
        this.redrawRanges();
        
        if(this.ranges.length < 5) {
            var elementId = 'addRange' + this.id;
            $(elementId).show();
        }
    },
    
    getHtmlSettings: function() {
        if(!this.isActivated)
            return null;
        
        var divSettings = new Element('div', {id: 'topicSettings'});
        
        //Append range header (add range and close)
        var divRangeFooter = new Element('div', {
            id: 'rangeFooter',
            style: 'display:block; height:32px; margin-left:-2px; width:270px;'
        });
        
        var btnClose = new Element('div', {
            'class': 'hideTopic',
            on: {
                    click: this.hideTopic
            },
            style: 'float: right; visibility:hidden;'
        });
        
        btnClose.append(new Element('img', {src: 'img/round_delete.png'}));
        
        divRangeFooter.append(btnClose);
        
        var rangeList = new Element('div', {
            id: 'rangeList' + this.id,
            'class': 'rangeList'
        });
        
        //append ranges
        if(this.supportRanges) {
        
            var addRangeDiv = new Element('div', {
                id: 'addRange' + this.id,
                style: 'display:block; line-height:32px; font-size: 12px;'
            });
            
            if(this.ranges.length >= 5)
                addRangeDiv.hide();
            
            var btnAddRange = new Element('div', {
                on: {
                        click: this.addNewRange
                },
                style: 'cursor:pointer; display:inline-block;'
            });
            
            btnAddRange.append(new Element('img', {
                src: 'img/round_plus_20.png',
                style: 'vertical-align: middle;height: 32px; width: 32px; margin-right: 2px;'
            }));
            
            btnAddRange.append('<span>Add Range</span>');
            
            addRangeDiv.append(btnAddRange);
            
            divRangeFooter.append(addRangeDiv);
            
            
            this.ranges.each(function(range) {
                range.appendHtml(rangeList);
            });
            
        }
        
        divSettings.append(rangeList);
        divSettings.append(divRangeFooter);
        
        return divSettings;
    },
    
    getQuery: function() {
        var statement = 'from='+ this.from;
        
        if(this.selectStatement)
            statement = statement + '|select=' + this.selectStatement;
        
        if(this.mode)
            statement = statement + '|mode=' + this.mode;
        
        if(this.icons)
            statement = statement + '|icons=' + this.icons;
        
        if(this.border)
            statement = statement + '|border=' + this.border;
        
        if(this.breaks)
            statement = statement + '|breaks=' + this.breaks;
        
        if(this.colors)
            statement = statement + '|colors=' + this.colors
        
        if(this.whereClause)
            statement = statement + '|where=' + this.whereClause;
        
        if(polygonLocations && polygonLocations.length > 0) {
            
            var locationsQuery = '';
            
            polygonLocations.each(function(p) {
                if(locationsQuery.length > 0)
                    locationsQuery = locationsQuery + ',';
                locationsQuery = locationsQuery + p.longitude + '%20' +p.latitude; 
            });
            
            statement = statement + "+intersects('POLYGON((" + locationsQuery + "))')";
        }
        else if(currentSelectedItem) {
            if(this.mode == "theme")
                statement = statement + '+pct_within(' + currentSelectedItem.id + ',%200.95)';
            else
                statement = statement + '+intersects(' + currentSelectedItem.id + ')';
        }
        
        return statement;
    },
    
    hideTopic: function() {
        this.isActivated = false;
        loadTileFromTopics();
        
        settingsTabPanel.remove(this.indexInTab);
        
        if(settingsTabPanel.tabs.length > 1)
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'visible');});
        else
            $$('div.hideTopic').each(function(button) {button.setStyle('visibility', 'hidden');});
    },
    
    onColorIndexChanged: function(index) {
        this.ranges.each(function(current) {
            current.colorIndex = index;
        });
        
        this.redrawRanges();
        Lightbox.hide();  
    },
    
    
    redrawRanges: function() {
        var rangeListId = 'rangeList'+this.id;
        $(rangeListId).clean();
        this.ranges.each(function(range) {
            range.appendHtml($(rangeListId));
        });
        
        this.computeBreaks();
        loadTileFromTopics();
    }
    
    
    
});

/******************************************
 * Topic Range class
 ******************************************/
var TopicRange = new Class({
    prebind: ['onMinChanged', 'onMaxChanged', 'onRemoveRangeClicked', 'showColorRamps'],
    
    //CTOR
    initialize: function(params) {
        for(var param in params) {
            switch(param) {
                case 'min':
                    this.min = params[param];
                    break;
                case 'max':
                    this.max = params[param];
                    break;
                case 'topic':
                    this.topic = params[param];
                    break;
                case 'id':
                    this.id = params[param];
                    break;
                case 'colorIndex':
                    this.colorIndex = params[param];
                    break;
                case 'rangeIndex':
                    this.rangeIndex = params[param];
                    break;
            }
        }
    },
    
    //Members
    colorIndex: 0,
    rangeIndex: 0,
    id: 0,
    min: 0,
    max: 0,
    topic: null,

    //Methods
    appendHtml: function(element) {
        var rangeDiv = new Element('div', {
            id: 'range'+this.id,
            'class': 'range'
        });
    
        rangeDiv.append(new Element('div', {
            'class': 'colorButton',
            on: {
                click: this.showColorRamps
            },
        }).setStyle('background-color','#'+colorRamps[this.colorIndex][this.rangeIndex]));
        
        rangeDiv.append(this.getDropDown('dropDownMin'+this.id, this.min, true));
        rangeDiv.append(' - ');
        rangeDiv.append(this.getDropDown('dropDownMax'+this.id, this.max, false));
        
        
        rangeDiv.append(new Element('div', {
                'class': 'infoButton',
                on: {
                    click: this.onRemoveRangeClicked    
                }
        }).append(new Element('img', {src:'img/round_delete.png'})));
        
        element.append(rangeDiv);
    },
    
    getDropDown: function(elementId, selectedValue, isMin) {
        var dropDown = new Element('select', {
            id: elementId,
            'class': 'dropDownMinMax'
        });
        
        if(isMin)
            dropDown.on('change',this.onMinChanged);
        else
            dropDown.on('change',this.onMaxChanged);
        
        if(this.topic.defaultMin == 0) {
            //starts from 0 and increment
            var currentValue = 0;
            for(var i=0; i<20; i++) {
                currentValue = i * this.topic.defaultIncrement;
                dropDown.append(new Element('option', {
                    value: currentValue
                }).append(currentValue + ' ' + this.topic.unitOfMeasure));
            }
        }
        else {
            //get an acceptable range that include a few values before min value and after max value
            var min = this.topic.defaultMin - (5 * this.topic.defaultIncrement);
            var max = this.topic.defaultMax + (5 * this.topic.defaultIncrement);
            
            for(var i=min; i<=max; i=i+this.topic.defaultIncrement) {
                dropDown.append(new Element('option', {
                    value: i
                }).append(i+ ' ' + this.topic.unitOfMeasure));
            }
        }
        
        
        if(selectedValue) {
            dropDown.set('value', selectedValue);
        }
        
        return dropDown;
    },
    
    getMax: function() {
        if(this.topic.handleAsPercentage)
            return this.max / 100;
        else
            return this.max;
    },
    
    getMin: function() {
        if(this.topic.handleAsPercentage)
            return this.min / 100;
        else
            return this.min;
    },
    
    onMinChanged: function() {
        var itemId = 'dropDownMin' + this.id;
        var selectedValue = $(itemId).get('value').toInt();
        this.min = selectedValue;
        this.topic.computeBreaks();
        loadTileFromTopics();
    },
    
    onMaxChanged: function() {
        var itemId = 'dropDownMax' + this.id;
        var selectedValue = $(itemId).get('value').toInt();
        this.max = selectedValue;
        this.topic.computeBreaks();
        loadTileFromTopics();
    },
    
    onRemoveRangeClicked: function() {
        if(this.topic.ranges.length > 1) {
            this.topic.deleteRange(this);
        }
    },
    
    showColorRamps: function() {
        
        var rampDiv = new Element('div');
        var header = new Element('p');
        header.append('Select a color range.');
        header.append(new Element('br'));
        
        //build the table on the fly
        var table = new Element('table', {
            'class': 'colorRamps'    
        });

        var tbody = new Element('tbody');
        table.append(tbody);
        
        var currentTopic = this.topic;
        var row = null;
        var cell = null;
        var cellContent = null;
        colorRamps.each(function(ramp) {
            row = new Element('tr', {
                on: {
                        click: function() {
                            currentTopic.onColorIndexChanged(this._.rowIndex);
                        }
                    }
            });
            
            tbody.append(row);
            ramp.each(function(color) {
                cell = new Element('td');
                cellContent = new Element('div');
                cellContent.setStyle('background-color','#'+color);
                cell.append(cellContent);
                row.append(cell);
            });
        });
        
        
        rampDiv.append(header);
        rampDiv.append(table);
        
        
        Lightbox.show(rampDiv);
    }
    
    
});

/******************************
 * Initialization
 ******************************/
function initialize() {
    
    //Process parameters
    oracle = isOracleModeRequested();
    
    //initialize all topics
    initializeSideBar();
    
    loadMap();
    
    $('settingsSidePanel').hide();
    $('settingsSidePanel').setStyle('visibility','visible');
    toggleTopicsVisibility();
    
    //initialize autocompleter
    extendAutoCompleter();
    setTimeout(function() {
        new Autocompleter('navTxtSearch', {
            url: proxy + 'spatialquery.json?apikey=oxagilee622d6575768fedc487d3beea&select=name%3badmin1%3bbounds%3bgeometryset%3barea%3bid%3bumi_hoods.bnds2010q1.st_prv_abbr%3bumi_hoods.bnds2010q1.county%3bumi_hoods.bnds2010q1.city%3bus_census00.zcta.id&from=umi_hoods.bnds2010q1_geom%3bus_census00.place_geom%3bus_census00.county_geom%3bus_census00.state_geom%3bus_census00.zcta_geom&order_by=-area&limit=10&where=name__istartswith:%27%{search}%27',
            onDone: function( ) {
                var current = this.first('li.current'); 
                if (current) {
                  var data = eval('('+ unescape(current.get('data')) + ')'); 
                  processSpatialQueryResult(data);
                }
            }
        });
    }, 2000);
    
    initializeColorRamps();
}

function extractQueryParameters() {
    // get the current URL
    var url = window.location.toString();
    // get the parameters
    url.match(/\?(.+)$/);
    var params = RegExp.$1;
    // split up the query string and store in an
    // associative array
    var params = params.split("&");
    return params;
}

function isOracleModeRequested() {
    var params = extractQueryParameters();
    return params.includes("oracle") || params.includes("Oracle") || params.includes("ORACLE");
    
}

function initializeColorRamps() {
    colorRamps[0] = ['ff5500','ffaa00','ffff00','aad500','55aa00'];
    colorRamps[1] = ['dc6b1e','b84d24','952f29','712339','4E1649'];
    colorRamps[2] = ['ffe692','febf5a','fd8d3c','f43d25','ca0923'];
    colorRamps[3] = ['e8e7f2','c6c7e1','9e9ac8','796eb2','796eb2'];
    colorRamps[4] = ['e1d4e8','cda0cd','df65b0','df2179','aa0649'];
    colorRamps[5] = ['ca4842','f7b799','000000','c7c7c7','747474'];
    colorRamps[6] = ['c8621f','9b58a5','a76854','bba90b','b17f19'];
    
}

function extendAutoCompleter() {
    
    Autocompleter.prototype.trigger = function() {

        this.timeout = null;
    
        this.cache = this.cache || {};
        var search = this.input.value(), options = this.options;
    
        if (search.length < options.minLength) { return this.hide(); }
    
        if (this.cache[search]) {
          this.suggest(this.cache[search], search);
        } else if (isArray(options.local)) {
          this.suggest(this.findLocal(search), search);
        } else {
          this.request = Xhr.load(options.url.replace('%{search}', encodeURIComponent(search)), {
            method:  options.method,
            spinner: this.getSpinner(),
            onComplete: (function(response) {
              var list = convertJsonResultToList(response.text);
              this.fire('load').suggest(list, search);
            }).bind(this)
          });
        }            
    };
    
}

function convertJsonResultToList(jsonResult) {
    try {
        var result = eval('('+jsonResult+')');
        if(result.success) {
            var list = '<ul>';
            var jsonData = null;
            result.data.each(function(entry) {
                    jsonData = escape('{id: ' + entry.id + ',geometryset: "' + entry.geometryset + '",bounds: ' + JSON.stringify(entry.bounds) + '}');
                    switch(entry.geometryset) {
                        case "us_census00.state_geom":
                            list = list + '<li data="' + jsonData + '">' + entry.name + '</li>';
                            break;
                        case "us_census00.place_geom":
                            list = list + '<li data="' + jsonData + '">' + entry.name + ', ' + entry.admin1 + '</li>';
                            break;
                        case "umi_hoods.bnds2010q1_geom":
                            list = list + '<li data="' + jsonData + '">' + entry.name + ', ' + entry['umi_hoods.bnds2010q1.city'] + ', ' + entry['umi_hoods.bnds2010q1.st_prv_abbr'] + '</li>';
                            break;
                        case "us_census00.zcta_geom":
                            list = list + '<li data="' + jsonData + '">' + entry['us_census00.zcta.id'] + '</li>';
                            break;
                        case "us_census00.county_geom":
                            list = list + '<li data="' + jsonData + '">' + entry.name + ' County, ' + entry.admin1 + '</li>';
                            break;
                    }
                });
            list = list + '</ul>';
            return list;
        }
        else {
            return "";
        }
    }
    catch (err) {
        console.log(err);
        return "";
    }
    
}

function initializeSideBar () {
    
    var sidebar = $('topics');
    
    initializeDefaultTopics();

    var table = new Element('table', {style: 'border-collapse:collapse;'});
    var newRow = true;
    var currentRow = null;

    topicGroups.each(function(topicGroup) {
        if(newRow) {
            currentRow = new Element('tr');
            newRow = false;
        }
        else {
            newRow = true;
        }
        
        table.insert(currentRow.insert(new Element('td').insert(topicGroup.getHtmlElement())));
    });
    
    sidebar.insert(table);
    
}

function initializeDefaultTopics() {
    
    var autoId = 1;
    topicGroups = new Array();
    topicGroups[0] = new TopicGroup('topicAQI',
                          'img/1.AirQualityIndex.png',
                          'img/1.AirQualityIndex_Active.png',
                          'Air Quality Index',
                          'The Air Quality Index (AQI) was developed by the US EPA to show major air pollutants regulated by the Clean Air Act (ground level ozone, particulate matter, carbon monoxide, sulphur dioxide and nitrogen dioxide). The index is divided into six classes: Good (0-50), Moderate (51-100), Unhealthy for Sensitive Groups (101-150), Unhealthy (151-200), Very Unhealthy (201-300) and Hazardous (301-500). It is aggregated at the County level.'
                          );
    topicGroups[0].addTopic(new Topic({
                            id: autoId++,
                            name: 'Air Quality Index (AQI)',
                            defaultMin: 0,
                            defaultMax: 100,
                            defaultIncrement: 50,
                            from: 'us_census00.county_geom',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //colors: '0000,ff5500,0000',
                            selectStatement: 'epa_airquality.county_data.med_aqi_value',
                            whereClauseTemplate: 'epa_airquality.county_data.med_aqi_value__gte:{@min}+epa_airquality.county_data.med_aqi_value__lt:{@max}',
                            group: topicGroups[0]
                          }));
    
    topicGroups[1] = new TopicGroup('topicADMT',
                          'img/2.AverageDailyMeanTemperature.png',
                          'img/2.AverageDailyMeanTemperature_Active.png',
                          'Average Daily Mean Temperature',
                          'Average Daily Mean Temperature is defined in January and July and an annual average is also provided. Measured in degrees Farenheit. This topic is collected only for populated places.'
                          );
    
    topicGroups[1].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 70,
                            defaultMax: 80,
                            defaultIncrement: 10,
                            unitOfMeasure: 'F',
                            name: 'Temperature ranges (July)',
                            from: 'us_census00.place_geom',
                            //colors: '0000,ca4842,0000',
                            mode: 'theme',
                            selectStatement: 'temp_avg_daily.places_daily_temp.julymean',
                            whereClauseTemplate: 'temp_avg_daily.places_daily_temp.julymean__gte:{@min}+temp_avg_daily.places_daily_temp.julymean__lt:{@max}',
                            group: topicGroups[1]
                          }));
    topicGroups[1].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 30,
                            defaultMax: 40,
                            defaultIncrement: 10,
                            unitOfMeasure: 'F',
                            name: 'Temperature ranges (January)',
                            from: 'us_census00.place_geom',
                            //colors: '0000,ca4842,0000',
                            mode: 'theme',
                            selectStatement: 'temp_avg_daily.places_daily_temp.januarymean',
                            whereClauseTemplate: 'temp_avg_daily.places_daily_temp.januarymean__gte:{@min}+temp_avg_daily.places_daily_temp.januarymean__lt:{@max}',
                            group: topicGroups[1]
                          }));
    topicGroups[1].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 60,
                            defaultMax: 70,
                            defaultIncrement: 10,
                            unitOfMeasure: 'F',
                            name: 'Temperature ranges (Annual)',
                            from: 'us_census00.place_geom',
                            //colors: '0000,ca4842,0000',
                            mode: 'theme',
                            selectStatement: 'temp_avg_daily.places_daily_temp.annualmean',
                            whereClauseTemplate: 'temp_avg_daily.places_daily_temp.annualmean__gte:{@min}+temp_avg_daily.places_daily_temp.annualmean__lt:{@max}',
                            group: topicGroups[1]
                          }));
    
    topicGroups[2] = new TopicGroup('topicTI',
                          'img/3.TransportationInfrastructure.png',
                          'img/3.TransportationInfrastructure_Active.png',
                          'Transportation Infrastructure',
                          'Transportation Infrastructure includes commercial airports, subway, commuter, light rail stations and routes.'
                          );
    topicGroups[2].addTopic(new Topic({
                            id: autoId++,
                            name: 'All Passenger Rail Lines',
                            supportRanges: false,
                            from: 'umi_mass_transit.network_edge_geom|mode=simple|colors=707070_2/from=umi_mass_transit.network_edge_geom|mode=saved|id=21',
                            //mode: 'simple',
                            //colors: '2_707070',
                            forceLayerIndex: true,
                            layerIndex: -1,
                            group: topicGroups[2]
                            }));
    topicGroups[2].addTopic(new Topic({
                            id: autoId++,
                            name: 'Commercial Airports',
                            supportRanges: false,
                            from: 'ntad_airports.arpt_loc_geom',
                            mode: 'simple',
                            icons: 'icon_air_transportation',
                            whereClause: 'ntad_airports.arpt_loc.tot_enplane__gte:40000',
                            group: topicGroups[2]
                            }));
    topicGroups[2].addTopic(new Topic({
                            id: autoId++,
                            name: 'Subway Stations',
                            supportRanges: false,
                            from: 'umi_mass_transit.stn_geom',
                            mode: 'simple',
                            icons: 'icon_train_in_circle',
                            whereClause: 'umi_mass_transit.stn.line_types_served__in:[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121,123,125]',
                            group: topicGroups[2]
                            }));
    topicGroups[2].addTopic(new Topic({
                            id: autoId++,
                            name: 'Light Rail Stations',
                            supportRanges: false,
                            from: 'umi_mass_transit.stn_geom',
                            select: 'umi_mass_transit.stn.line_types_served',
                            mode: 'simple',
                            icons: 'icon_metro_front',
                            whereClause: 'umi_mass_transit.stn.line_types_served__in:[2,3,6,7,10,11,14,15,18,19,22,23,26,27,30,31,34,35,38,39,42,43,46,47,50,51,54,55,58,59,62,63,66,67,70,71,74,75,78,79,82,83,86,87,90,91,94,95,98,99,102,103,106,107,110,111,114,115,118,119,122,123,126]',
                            group: topicGroups[2]
                            }));
    topicGroups[2].addTopic(new Topic({
                            id: autoId++,
                            name: 'Commuter Rail Stations',
                            supportRanges: false,
                            from: 'umi_mass_transit.stn_geom',
                            select: 'umi_mass_transit.stn.line_types_served',
                            mode: 'simple',
                            icons: 'icon_rail_transportation',
                            whereClause: 'umi_mass_transit.stn.line_types_served__in:[16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126]',
                            group: topicGroups[2]
                            }));
    

    
    topicGroups[3] = new TopicGroup('topicRA',
                          'img/4.ReligiousAffiliation.png',
                          'img/4.ReligiousAffiliation_Active.png',
                          'Religious Affiliation',
                          'Religious Affiliation is aggregated at the county level and divided into 149 groups. Religious adherents are defined as those who hold membership with the specific religious group.'
                          );
    topicGroups[3].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 20,
                            defaultIncrement: 20,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Percentage of Population Religiously Active',
                            from: 'us_census00.county_geom',
                            selectStatement: 'religion.county_data.prt_adherents',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //colors: '0000,c8621f,0000',
                            whereClauseTemplate: 'religion.county_data.prt_adherents__gte:{@min}+religion.county_data.prt_adherents__lt:{@max}',
                            group: topicGroups[3]
                          }));
    
    topicGroups[4] = new TopicGroup('topicSEP',
                          'img/5.SchoolEducationalPerformance.png',
                          'img/5.SchoolEducationalPerformance_Active.png',
                          'School Educational Performance',
                          'School and Educational Performance data is from Education.com. The TestRating is a 1-10 rating that provides an overview of a school' +"'" +'s test performance for a given year, by comparing the school' + "'" +'s state standardized test results to those of other schools in the same state. A value of 10 is the highest-performing school.'
                          );
    topicGroups[4].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 6000,
                            defaultIncrement: 3000,
                            name: 'Expenditures per Pupil (District Level)',
                            from: 'us_schl_dists.dist_geom',
                            selectStatement: 'schoolperfed_com.dist_stats@2006.cost_per_st',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'schoolperfed_com.dist_stats@2006.cost_per_st__gte:{@min}+schoolperfed_com.dist_stats@2006.cost_per_st__lt:{@max}',
                            group: topicGroups[4]
                          }));
    topicGroups[4].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 5,
                            defaultIncrement: 5,
                            name: 'Student/Teacher Ratio (District Level)',
                            from: 'us_schl_dists.dist_geom',
                            selectStatement: 'schoolperfed_com.dist_stats@2007.st_tchr_ratio',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'schoolperfed_com.dist_stats@2007.st_tchr_ratio__gte:{@min}+schoolperfed_com.dist_stats@2007.st_tchr_ratio__lt:{@max}',
                            group: topicGroups[4]
                          }));
    topicGroups[4].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 2,
                            defaultIncrement: 2,
                            name: 'Test Rating',
                            from: 'schoolperfed_com.sch_info_geom',
                            selectStatement: 'schoolperfed_com.sch_info.ranking',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'schoolperfed_com.sch_info.ranking__gte:{@min}+schoolperfed_com.sch_info.ranking__lt:{@max}',
                            group: topicGroups[4]
                          }));
    
    topicGroups[5] = new TopicGroup('topicTR',
                          'img/6.TaxRates.png',
                          'img/6.TaxRates_Active.png',
                          'Tax Rates',
                          'Tax rates includes local, county and sales tax, aggregated at the County level.'
                          );
    topicGroups[5].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 1,
                            defaultIncrement: 1,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Sales Tax ranges',
                            from: 'us_census00.county_geom',
                            selectStatement: 'sales_tax.county_data.state_sales_tax',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,0.01',
                            //colors: '0000,e1d4e8,0000',
                            whereClauseTemplate: 'sales_tax.county_data.state_sales_tax__gte:{@min}+sales_tax.county_data.state_sales_tax__lt:{@max}',
                            group: topicGroups[5]
                          }));
    
    topicGroups[6] = new TopicGroup('topicUR',
                          'img/7.UnemploymentRate.png',
                          'img/7.UnemploymentRate_Active.png',
                          'Unemployment Rate',
                          'Unemployment Rate as reported monthly by the Bureau of Labor Statistics. It is aggregated at the county level.'
                          );
    topicGroups[6].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 5,
                            defaultIncrement: 5,
                            unitOfMeasure: '%',
                            name: 'Unemployment rate',
                            from: 'us_census00.county_geom',
                            selectStatement: 'unemplmnt.data.unemprate',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5',
                            //colors: '0000,c8621f,0000',
                            whereClauseTemplate: 'unemplmnt.data.unemprate__gte:{@min}+unemplmnt.data.unemprate__lt:{@max}',
                            group: topicGroups[6]
                          }));
    
    topicGroups[7] = new TopicGroup('topicBRVR',
                          'img/8.BusinessResidentialVacancyRate.png',
                          'img/8.BusinessResidentialVacancyRate_Active.png',
                          'Business and Residential Vacancy Rates',
                          'Business and Residential Vacancy Rates track business and residential units that have been vacant over the past 12 months.'
                          );
    topicGroups[7].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 5,
                            defaultIncrement: 5,
                            unitOfMeasure: '%',
                            name: 'The percent of units that are vacant',
                            from: 'us_census00.tract_geom',
                            selectStatement: 'bus_res_vacancy.tract_data.pct_vac',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5',
                            //colors: '0000,dc6b1e,0000',
                            whereClauseTemplate: 'bus_res_vacancy.tract_data.pct_vac__gte:{@min}+bus_res_vacancy.tract_data.pct_vac__lt:{@max}',
                            group: topicGroups[7]
                          }));
    
    topicGroups[8] = new TopicGroup('topicPE',
                          'img/9.PresidentialElection.png',
                          'img/9.PresidentialElection_Active.png',
                          'Presidential Election',
                          'Presidential Election returns are from the 2008 election and include major party results by County. Percentage voter turnout in the 2008 general election is also included. Wyoming reports turnout percentages, which can be greater than 100% if unregistered voters vote. North Dakota and Wisconsin do not report turnout.'
                          );
    topicGroups[8].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 60,
                            defaultIncrement: 10,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Percentage Voter Turnout',
                            from: 'us_census00.county_geom',
                            selectStatement: 'uselect_turnout.county_data.prt_turnout_reg',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,0.65',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'uselect_turnout.county_data.prt_turnout_reg__gte:{@min}+uselect_turnout.county_data.prt_turnout_reg__lt:{@max}',
                            group: topicGroups[8]
                          }));
    topicGroups[8].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 40,
                            defaultIncrement: 10,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Democratic Party (Obama)',
                            from: 'us_census00.county_geom',
                            selectStatement: 'uselect_pres.county_data.prt_obama_votes',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,0.65',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'uselect_pres.county_data.prt_obama_votes__gte:{@min}+uselect_pres.county_data.prt_obama_votes__lt:{@max}',
                            group: topicGroups[8]
                          }));
    topicGroups[8].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 40,
                            defaultIncrement: 10,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Republican Party (McCain)',
                            from: 'us_census00.county_geom',
                            selectStatement: 'uselect_pres.county_data.prt_mccain_votes',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,0.65',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'uselect_pres.county_data.prt_mccain_votes__gte:{@min}+uselect_pres.county_data.prt_mccain_votes__lt:{@max}',
                            group: topicGroups[8]
                          }));
    topicGroups[8].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 40,
                            defaultIncrement: 10,
                            handleAsPercentage: true,
                            unitOfMeasure: '%',
                            name: 'Other parties',
                            from: 'us_census00.county_geom',
                            selectStatement: 'uselect_pres.county_data.prt_thirdparty_votes',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,0.65',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'uselect_pres.county_data.prt_thirdparty_votes__gte:{@min}+uselect_pres.county_data.prt_thirdparty_votes__lt:{@max}',
                            group: topicGroups[8]
                          }));
    
    topicGroups[9] = new TopicGroup('topicEH',
                          'img/10.EnvironmentalHazards.png',
                          'img/10.EnvironmentalHazards_Active.png',
                          'Environmental Hazards',
                          'The Environmental Hazards topic represents coastal vulnerability to sea level rise index (CVI), tornado touchdown points and earthquakes. The CVI ranges from 1 (Very Low) to 5 (Very High), tornados rated on the Fujita Damage Scale from 0 (Gale) to 5 (Incredible). Earthquake data are represented by the Richter Scale from 4 (Light) to 10 (Epic).'
                          );
    topicGroups[9].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 2,
                            defaultIncrement: 1,
                            name: 'CVI',
                            from: 'us_cvi.risk_mdl_geom',
                            selectStatement: 'us_cvi.risk_mdl.cvi_risk',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5',
                            colorSuffix: '_1.5',
                            //colors: '0000_1.5,ff5500_1.5,0000_1.5',
                            whereClauseTemplate: 'us_cvi.risk_mdl.cvi_risk__gte:{@min}+us_cvi.risk_mdl.cvi_risk__lt:{@max}',
                            group: topicGroups[9]
                          }));
    topicGroups[9].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 3,
                            defaultIncrement: 1,
                            name: 'Tornado touchdowns',
                            from: 'noaa_tornado.touchdowns_geom',
                            selectStatement: 'noaa_tornado.touchdowns.f_scale',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5',
                            //colorSuffix: '_1.5',
                            //colors: '0000_1.5,ff5500_1.5,0000_1.5',
                            whereClauseTemplate: 'noaa_tornado.touchdowns.f_scale__gte:1+noaa_tornado.touchdowns.f_scale__lt:{@max}',
                            group: topicGroups[9]
                          }));
    topicGroups[9].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 3,
                            defaultIncrement: 1,
                            name: 'Earthquakes epicenters',
                            from: 'earthquakes.epicenters_geom',
                            selectStatement: 'earthquakes.epicenters.eq_scale',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5',
                            //colorSuffix: '',
                            //colors: '0000_1.5,ff5500_1.5,0000_1.5',
                            whereClauseTemplate: 'earthquakes.epicenters.eq_scale__gte:{@min}+earthquakes.epicenters.eq_scale__lt:{@max}',
                            group: topicGroups[9]
                          }));
    
    topicGroups[10] = new TopicGroup('topicCL',
                          'img/11.CostLiving.png',
                          'img/11.CostLiving_Active.png',
                          'Cost of Living',
                          'The Cost of living index is represented as an indexed value (100 is the US national average) for a composite and five subcomponents: transportation, utilities, health care expenditures, miscellaneous and groceries. Note that metropolitan areas only (approximately 300) are represented.'
                          );
    topicGroups[10].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 75,
                            defaultMax: 90,
                            defaultIncrement: 15,
                            name: 'Composite cost of living index',
                            from: 'us_census00.place_geom',//'us_census00.cbsa_geom',
                            selectStatement: 'us_coli.qrtr_ind.composite_index',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '75,90',
                            //colors: '0000,ff5500,0000',
                            //colorPadding: '0000',
                            whereClauseTemplate: 'us_coli.qrtr_ind.composite_index__gte:{@min}+us_coli.qrtr_ind.composite_index__lt:{@max}',
                            group: topicGroups[10]
                          }));

    topicGroups[11] = new TopicGroup('topicDD',
                          'img/12.DemographicsData.png',
                          'img/12.DemographicsData_Active.png',
                          'Demographics Data',
                          'Demographics data are compiled into five general categories: Population & Race, Affluence & Education, Housing & Household Composition, Age by Sex by Race, and Employment & Occupation.'
                          );
    topicGroups[11].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 5000,
                            defaultIncrement: 5000,
                            name: 'Total population',
                            from: 'us_census00.county_geom',
                            selectStatement: 'geolytics.income.etot_pop',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5000',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'geolytics.income.etot_pop__gte:{@min}+geolytics.income.etot_pop__lt:{@max}',
                            group: topicGroups[11]
                          }));
    topicGroups[11].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 30000,
                            defaultIncrement: 10000,
                            name: 'Estimated Family Income',
                            from: 'us_census00.county_geom',
                            selectStatement: 'geolytics.income.efaminpov',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5000',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'geolytics.income.efaminpov__gte:{@min}+geolytics.income.efaminpov__lt:{@max}',
                            group: topicGroups[11]
                          }));
    
    topicGroups[11].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 500,
                            defaultIncrement: 500,
                            name: 'Families in poverty',
                            from: 'us_census00.county_geom',
                            selectStatement: 'geolytics.income.efaminpov',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5000',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'geolytics.income.efaminpov__gte:{@min}+geolytics.income.efaminpov__lt:{@max}',
                            group: topicGroups[11]
                          }));
    topicGroups[11].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 3000,
                            defaultIncrement: 3000,
                            name: 'Total housing units',
                            from: 'us_census00.county_geom',
                            selectStatement: 'geolytics.housing.phutr',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5000',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'geolytics.housing.phutr__gte:{@min}+geolytics.housing.phutr__lt:{@max}',
                            group: topicGroups[11]
                          }));
    topicGroups[11].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 500,
                            defaultIncrement: 500,
                            name: 'Vacant housing units',
                            from: 'us_census00.county_geom',
                            selectStatement: 'geolytics.housing.phuv',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,5000',
                            //colors: '0000,ff5500,0000',
                            whereClauseTemplate: 'geolytics.housing.phuv__gte:{@min}+geolytics.housing.phuv__lt:{@max}',
                            group: topicGroups[11]
                          }));
    
    topicGroups[12] = new TopicGroup('topicAMS',
                          'img/13.AverageMonthlySnowfall.png',
                          'img/13.AverageMonthlySnowfall_Active.png',
                          'AverageMonthlySnowfall',
                          'Average Monthly Snowfall is defined in March and December of each year, and an annual average is also provided. Measured in inches. This topic is collected only for populated places.'
                          );
    topicGroups[12].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 50,
                            defaultIncrement: 10,
                            name: 'Snowfall ranges (March)',
                            from: 'us_census00.place_geom',
                            selectStatement: 'snowfall_monthly.data.maravg',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,50',
                            //colors: '0000,dc6b1e,0000',
                            whereClauseTemplate: 'snowfall_monthly.data.maravg__gte:{@min}+snowfall_monthly.data.maravg__lt:{@max}',
                            group: topicGroups[12]
                          }));
    
    topicGroups[13] = new TopicGroup('topicCE',
                          'img/14.ConsumerExpenditure.png',
                          'img/14.ConsumerExpenditure_Active.png',
                          'Consumer Expenditures',
                          'Consumer Expenditures provide information on the buying habits of American consumers. Includes data on expenditures across health care, transportation, housing, utility, grocery and other expenses.'
                          );
    topicGroups[13].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 1000,
                            defaultIncrement: 200,
                            unitOfMeasure: '$',
                            name: 'Electricity-related Expenditures',
                            from: 'us_census00.county_geom',
                            selectStatement: 'csmr_exp.data.ehelectr',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,1000',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'csmr_exp.data.ehelectr__gte:{@min}+csmr_exp.data.ehelectr__lt:{@max}',
                            group: topicGroups[13]
                          }));
    topicGroups[13].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 1200,
                            defaultIncrement: 100,
                            unitOfMeasure: '$',
                            name: 'Health Insurance-related Expenditures',
                            from: 'us_census00.county_geom',
                            selectStatement: 'csmr_exp.data.ehinsurnce',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,1000',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'csmr_exp.data.ehinsurnce__gte:{@min}+csmr_exp.data.ehinsurnce__lt:{@max}',
                            group: topicGroups[13]
                          }));
    topicGroups[13].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 1100,
                            defaultIncrement: 100,
                            unitOfMeasure: '$',
                            name: 'Property Tax-related Expenditures',
                            from: 'us_census00.county_geom',
                            selectStatement: 'csmr_exp.data.ehproptax',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,1000',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'csmr_exp.data.ehproptax__gte:{@min}+csmr_exp.data.ehproptax__lt:{@max}',
                            group: topicGroups[13]
                          }));
    topicGroups[13].addTopic(new Topic({
                            id: autoId++,
                            defaultMin: 0,
                            defaultMax: 30000,
                            defaultIncrement: 10000,
                            unitOfMeasure: '$',
                            name: 'Average Household Expenditures',
                            from: 'us_census00.county_geom',
                            selectStatement: 'csmr_exp.data.ehsehldexp',
                            mode: 'theme',
                            border: 'eee_0.25',
                            //breaks: '0,1000',
                            //colors: '0000,ffe692,0000',
                            whereClauseTemplate: 'csmr_exp.data.ehsehldexp__gte:{@min}+csmr_exp.data.ehsehldexp__lt:{@max}',
                            group: topicGroups[13]
                          }));
    
}

function loadMap() {
    
    // Initialize the map
    var apiKey = "Aha-yAfw-31xtfv_Bt_tcBkRdEyGGHh8XnlNNKF42ypDZw3XmPVarwTatNhpsg-1";
    var mapOptions = {
        credentials: apiKey,
        center: new Microsoft.Maps.Location(38.030785, -97.734375),
        zoom: 5,
        mapTypeId: Microsoft.Maps.MapTypeId.road,
        showDashboard: false,
        showScalebar: false,
        enableSearchLogo: false
    }
    
                
    map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), mapOptions);
    
    Microsoft.Maps.Events.addHandler(map, "viewchangestart", onViewChangeStart);
    
    if(oracle) {
        loadOracleTile();
        setTimeout("$$('.LogoContainer')[0].setStyle('display','none')", 100);
    }
}

function onViewChangeStart() {
    if (map.getZoom() >= 18) 
    {
        //console.log('***' + map.getZoom());
        map.setView({
          'zoom':       18,
          'animate':    false
        });
    }
}


/******************************
 * Tile Management
 ******************************/  
function loadTileFromTopics() {
    try {
        map.entities.clear();
        
        var tilePath = getTilesPath();
        loadTile(tilePath, layerOpacity);
        
        if(polygonLocations && polygonLocations.length > 1) {
            var polygoncolor = new Microsoft.Maps.Color(255,58,69,232);
            var fillcolor = new Microsoft.Maps.Color(0,0,0,0);
            polygon = new Microsoft.Maps.Polygon(polygonLocations,{fillColor: fillcolor, strokeColor: polygoncolor});
            map.entities.push(polygon);
        }
        
        
        if(oracle)
            loadOracleTile();
    }
    catch(err) {
        console.log(err);
    }
}

function getTilesPath() {
    
    var tilesPath = null;
    var topics = new Array();
    
    topicGroups.each(function(topicGroup) {
        
        if(topicGroup.isActivated) {
            
            topicGroup.topics.each(function(topic) {
                if(topic.isActivated) 
                    topics.push(topic);
            });
        }
    });
    
    topics.sortBy('layerIndex').each(function(topic){
        if(tilesPath)
            tilesPath = tilesPath + "/" + topic.getQuery();
        else
            tilesPath = topic.getQuery();
    });
    
    if(currentSelectedItem) {
        
        var polygonQuery = 'from=' + currentSelectedItem.geometryset +'|mode=simple|border=00f_4|colors=609ce400|where=id__iexact:' + currentSelectedItem.id;
        
        if(tilesPath)
            tilesPath = tilesPath + '/' + polygonQuery;
        else
            tilesPath = polygonQuery;
        
    }
    
    if(tilesPath) { 
        tilesPath = 'http://visual.mapfluence.com/tile/d/' + tilesPath + '/bing/r{quadkey}.png?apikey=oxagilee622d6575768fedc487d3beea';
    }
    
    return tilesPath;
}

function loadTile(tilePath, tileOpacity) {
    if(tilePath) {
        var tileSource = new Microsoft.Maps.TileSource({uriConstructor: tilePath}); 
        // Construct the layer using the tile source
        var tilelayer= new Microsoft.Maps.TileLayer({ mercator: tileSource, opacity: tileOpacity });  
        map.entities.push(tilelayer);
    }
}

function loadOracleTile() {
    var oracleTileSource = new Microsoft.Maps.TileSource({uriConstructor: getOracleTilePath}); 
    // Construct the layer using the tile source
    var tilelayer= new Microsoft.Maps.TileLayer({ mercator: oracleTileSource, opacity: 1 });  
    map.entities.push(tilelayer);
}

function getOracleTilePath(tile) {
    var url = "http://maps.oracle.com/mapviewer/mcserver?request=gettile&format=PNG&zoomlevel=" + (tile.levelOfDetail-1) + "&mapcache=ELOCATION_MERCATOR.WORLD_MAP&mx=" + tile.x + "&my=" + (Math.pow(2,tile.levelOfDetail)-tile.y-1);  
  return url;
}


function processSpatialQueryResult(jsonResult) {

    currentSelectedItem = jsonResult;
    
    //Set boundaries
    if(jsonResult.bounds && jsonResult.bounds.length > 3) {
        var boundaries = Microsoft.Maps.LocationRect.fromLocations(new Microsoft.Maps.Location(jsonResult.bounds[1], jsonResult.bounds[0]), new Microsoft.Maps.Location(jsonResult.bounds[3], jsonResult.bounds[2]));
        map.setView({ bounds: boundaries});
        loadTileFromTopics();
    }
    
    //display the button
    $('btnClearRegion').setClass('toolbarButton highlight');
}

function clearRegionFilter() {
    polygonLocations = null;
    currentSelectedItem = null;
    $('navTxtSearch').set('value','');
    loadTileFromTopics();
    $('btnClearRegion').setClass('toolbarButton hidden');
}

function toggleSettingsVisibility() {
    //$('settings').slide({direction: 'left'});
    if(!isSettingsPanelVisible) {
        $('settingsSidePanel').show();
        $('btnToggleSettings').setClass('toolbarButton selected');
        if($('settings').children().length == 0) {
            var message = new Element('span', {'id': 'emptySettings'})
                .text('Please select a topic.')
                .insertTo($('settings'));
        }
    }
    else {
        $('settingsSidePanel').hide();
        $('btnToggleSettings').setClass('toolbarButton');
    }
    isSettingsPanelVisible = !isSettingsPanelVisible;
}

function toggleTopicsVisibility() {
    //$('settings').slide({direction: 'left'});
    if(!isTopicsPanelVisible) {
        $('topicSidePanel').show();
        $('btnToggleTopics').setClass('toolbarButton selected');
    }
    else {
        hideSettings();
        $('topicSidePanel').hide();
        $('btnToggleTopics').setClass('toolbarButton');
    }
    isTopicsPanelVisible = !isTopicsPanelVisible;
}

function showSettings() {
    if(!isSettingsPanelVisible)
        toggleSettingsVisibility();
}

function hideSettings() {
    if(isSettingsPanelVisible)
        toggleSettingsVisibility();
}

function clearSettings() {
    $('settings').clean();
    hideSettings();
}

/******************************
 * Nav Bar Actions
 ******************************/
function zoomIn() {
    var zoomLevel = map.getZoom() + 1;
    map.setView({zoom:zoomLevel});
}

function zoomOut() {
    var zoomLevel = map.getZoom() - 1;
    map.setView({zoom:zoomLevel});
}

function showAbout() {
    
    var aboutDiv = new Element('div', {
        style: 'width: 350px; height: 320px;'    
    });
    var aboutHtml = '<a tabindex="0" style:"text-decoration:none;" target="_blank" href="http://urbanmapping.com/"><img src="http://www.geofactfinder.com/entryloading/um_logo.gif" border=0 /></a><div><span>Geo Fact Finder is designed to illustrate capabilities of </span><a tabindex="0" target="_blank" href="http://urbanmapping.com/products/mapfluence/">Mapfluence</a><span>, our hosted mapping platform. Mapfluence is a web-based geoservices engine designed to push geo-intelligence to the browser. We also maintain a </span><a tabindex="0" target="_blank" href="http://developer.urbanmapping.com/content/Data-Catalog/">data catalog</a><span>, providing immediate and query-able access to geographic data. This application is intended to visualize and explore geographic data; we' + "'" +'ve selected a subset of our 10,000+ variables and included them in different topic areas. We look forward to your feedback and please don' + "'"+'t hesitate to </span><a tabindex="0" target="_blank" href="http://www.urbanmapping.com/about-urban-mapping/contact-form/">contact us</a><span> with any questions. You can send email to </span><a tabindex="0" target="_blank" href="mailto:info@urbanmapping.com">info@urbanmapping.com</a><span>.</span>';
    var about = new Element('div').html = aboutHtml;
    
    aboutDiv.append(about);
    aboutDiv.append(new Element('br'));
    
    Lightbox.show(aboutDiv);
}


function drawPolygon() {
    if(drawing)
        endPolygon();
    else
        beginPolygon();
}

function beginPolygon() {
    drawing = true;
    $('btnDrawPolygon').setClass('toolbarButton selectedMustFollow');
    $('btnDrawPolygon').first('img').set('src','img/round_checkmark.png');
    
    if(polygon) {
        map.entities.remove(polygon);
        polygon = null;
    }
    
    polygonLocations = new Array();
    //set map in a mode where user can add pushpin... and record pushpin so we can create a polygon...
    mapClickHandler = Microsoft.Maps.Events.addHandler(map, 'click', onMapMouseClicked);

}
function endPolygon() {
    drawing = false;
    Microsoft.Maps.Events.removeHandler(mapClickHandler);
    $('btnDrawPolygon').setClass('toolbarButton');
    $('btnDrawPolygon').first('img').set('src','img/doc_edit.png');
    
    if(!polygonLocations || polygonLocations.length <= 0)
        return;
    
    $('btnClearRegion').setClass('toolbarButton highlight');
    
    polygonLocations.push(polygonLocations[0]);
    
    loadTileFromTopics();
}

function onMapMouseClicked(e) {
    if(!drawing)
        return;
    
    var x = e.getX();
    var y = e.getY();
    var location = map.tryPixelToLocation(new Microsoft.Maps.Point(x, y));
    
    if(!polygonLocations)
        polygonLocations = new Array();
    
    polygonLocations.push(location);
    
    if(polygonLocations.length > 1) {
        var polygoncolor = new Microsoft.Maps.Color(255,58,69,232);
        var polyline = new Microsoft.Maps.Polyline(new Array(polygonLocations[polygonLocations.length -2 ], polygonLocations[polygonLocations.length -1 ]),{strokeColor: polygoncolor});
        map.entities.push(polyline);
    }
    
    var pushpin = new Microsoft.Maps.Pushpin(location);
    map.entities.push(pushpin);
}

function opacityChanged() {
    layerOpacity = $('cmbOpacity').get('value').toFloat();
    loadTileFromTopics();
}
