<?xml version="1.0" encoding="UTF-8"?>
<!-- get_data.xml -->
<!-- Author: Ian Smith (imsmith@uw.edu) -->
<Module>
<ModulePrefs title="Plot">
    <Require feature="locked-domain" />  
    <Require feature="dynamic-height"/>
    <Require feature="opensocial-0.9"/> 
    <Require feature="minimessage"/>
    <Require feature="settitle"/>
</ModulePrefs>
<Content type="html"><![CDATA[

<link href="css/gadget.css" rel="stylesheet" type="text/css">

<script src="js/DataInterface.js" type="text/javascript"></script>
<script src="js/astroObjectLib.js" type="text/javascript"></script>
<script src="js/dataset.js" type="text/javascript"></script>


<div id="divQuery" style="width: 100%;">
    Dataset Name: <input id="txtDatasetName" type="text" />
    <div id="divStatusText" class="statusBox"></div> 
</div>

<div id="divVPBounds" style="float:right;" class="infoBox">
    <h3>Viewport Bounds</h3>
    <span id="spViewportBounds" class="info"></span>
</div>


<div id="divQueryButtons">
</div>

<div id="divCatalog">
    Catalog:  <select id="selCatalog">
              </select>
</div>

<div id="divQueryBox" style="width:100%;">
    Query String: 
    <br/> 
    <textarea id="txtQueryString" rows="5" style="width:100%;"></textarea>
    <br/>
    <input id="btGetData" onclick="ga.getData();" type="button" value="Submit"/>
    
</div>


<script type="text/javascript">
    

var ga = null,
    DEBUG = false;

function debugLog(msg){ if(DEBUG){UW.astro.debugLog(GADGET_NAME + ": " + msg);}}


// GLOBAL CONSTANTS
var GADGET_NAME = "Get_Data" + "_" + Math.floor(Math.random()*100000).toString();

// Text in {{ }} such as " {{ra.lower}}" are substituted in setQueryText() method
var QUERY_BUTTONS =  [ { buttonNum: 0,
                         name: "SDSS Stars",
                         desc: "Get star magnitudes in U, G, R, I, Z from SDSS",
                         catalog: "SDSS",
                         query:  "SELECT TOP 500 objid, ra, dec, modelMag_u, modelMag_g, modelMag_r, modelMag_i, modelMag_z, (modelMag_u - modelMag_g) as u_g, (modelMag_g - modelMag_r) as g_r, (modelMag_r - modelMag_i) as r_i, (modelMag_i - modelMag_z) as i_z from Star where ra between {{ra.lower}} and {{ra.upper}} and dec between {{dec.lower}} and {{dec.upper}} and modelMag_g > -9999 ORDER BY modelMag_g ASC"
                       },
                       { buttonNum: 1,
                         name: "SDSS Galaxies",
                         desc: "Get galaxy magnitudes in U, G, R, I, Z from SDSS",
                         catalog: "SDSS",
                         query: "SELECT TOP 500 g.objid, g.ra, g.dec, s.z, g.modelMag_u, g.modelMag_g, g.modelMag_r, g.modelMag_i, g.modelMag_z, (g.modelMag_u - g.modelMag_g) as u_g, (g.modelMag_g - g.modelMag_r) as g_r, (g.modelMag_r - g.modelMag_i) as r_i, (g.modelMag_i - g.modelMag_z) as i_z from Galaxy as g left outer join SpecObjAll as s on g.specobjID = s.specobjID where g.ra between {{ra.lower}} and {{ra.upper}} and g.dec between {{dec.lower}} and {{dec.upper}} and g.modelMag_g > -9999 ORDER BY g.modelMag_g ASC"
                       }];
//
//                       { buttonNum: 2,
//                         name: "2MASS 1",
//                         desc: "2MASS 1",
//                         catalog: "2MASS",
//                         query: "Under Construction"
//                       }
//                    ];
//
   


// create HR object
function init(){
    gadgets.window.setTitle("Data Query");
    ga = new GetData();
    ga.setFields();
    gadgets.window.adjustHeight();
}


function GetData(){
    var my = new Object();

    var _myFields = {};
    // CONSTANTS
        var PLOT_HEIGHT = 400,
        PLOT_WIDTH = 300;

    // Status Icons
    var ICON_URL_SELECTED = '/ascot/gadgets/images/target_yellow.png',
        ICON_URL_DEFAULT = '/ascot/gadgets/images/target.png',
        ICON_URL_SPINNER = '/ascot/gadgets/images/spinnerGrey.gif',
        ICON_URL_ERROR = '/ascot/gadgets/images/errorIcon.gif',
        ICON_URL_SUCCESS = '/ascot/gadgets/images/successIcon.gif';

    var ICON = { SUCCESS : ICON_URL_SUCCESS,
              ERROR : ICON_URL_ERROR,
              SPINNER : ICON_URL_SPINNER,
              NONE : ""}
 
    // PRIVATE
    var _namespaceList = [ UW.astro.DEFAULT_NAMESPACE ];      // list of our current Namespaces. Just init with the default
    var _catalogs =  [ { name: "SDSS", url: "http://cas.sdss.org/public/en/tools/search/x_sql.asp"  },         // contains info for catalogs and thier urls
                      // { name: "2MASS", url: "" } 
                    ];
        
    
    // Private Helper Variables
    var di = new UW.astro.DataInterface(GADGET_NAME); // make dataInterface object, init with default

    // INIT
    di.registerMe( { namespaceList : _namespaceList,
                    variableList : [ "Dataset_List", "Viewport_Current_Bounds" ],
                    triggerList : [ [ "Viewport_Current_Bounds", setVPBounds] ],
                } );

    
   function getDatasetNameText () {
        return document.getElementById("txtDatasetName").value;
    }
    
    function clearForm() {
        document.getElementById("txtDatasetName").value = '';
        // no reason to clear query string yet
        // document.getElementById("txtQueryString").value = '';
    }
        
    // PUBLIC METHODS  
    my.setFields = function() {
        // set the catalog options
        var  options = [];
        for (var i=0, len=_catalogs.length; i<len; i++) {
            options.push(_catalogs[i].name);
        }
        setSelectOptions("selCatalog", options);

        // create the buttons
        for (var i=0, len=QUERY_BUTTONS.length; i<len; i++) {
            createQueryButton(QUERY_BUTTONS[i]);
        }
    }
    function createQueryButton(buttonObj) {
        var divButtons = document.getElementById('divQueryButtons');
        var button = document.createElement('input');
        button.setAttribute('type', 'button');
        button.setAttribute('name', buttonObj.name);
        button.setAttribute('value', buttonObj.name);
        button.setAttribute('title', buttonObj.desc);
        button.onclick = function(){ ga.setQueryText(buttonObj.buttonNum); };
        divButtons.appendChild(button);
    }
    
    my.getData = function() {
        var dsName = getDatasetNameText();
        if (dsName.length < 1) {
            showError("Enter dataset name");
            return;
        }
 
        // TODO: other ways to choose the bounds we want on the gadget
        showSpinner("Getting objects..."); 
        
        // get query string from the text area
        var queryObj = {data: {cmd:null, format:null}, url: null};
        queryObj.data.cmd = getQueryString();
        if (queryObj.data.cmd === "") {
            showError('Enter query string');
            return;
        }
       
        // find catalog in the _catalogs object to get the url
        var selectedCatalog = getSelectedOption("selCatalog");
        for (var i=0, len=_catalogs.length; i<len; i++) {
            if (_catalogs[i].name === selectedCatalog) {
                queryObj.url = _catalogs[i].url;
            }
        }
        // set to XML (for SDSS anyway)
        queryObj.data.format = "xml";
        
        if (queryObj.url === null) {
            showError('Select a catalog');
            return;
        }
        // finishes with callback "queryResponseCB"
        makeGETRequest(queryObj.url, queryObj.data, queryResponseCB);
    }
    
    my.setQueryText = function(buttonNum) {
        if (QUERY_BUTTONS.length > buttonNum) {
            // set catalog
            var buttonObj = QUERY_BUTTONS[buttonNum];
            
            setSelectedCatalog(buttonObj.catalog);
            
            var qString = buttonObj.query;

            // replace bounds    
            var bounds = getVPBounds();
            if (bounds !== null) {
                qString = replaceQueryVars(qString, "{{ra.lower}}", bounds[0].getRa().toFixed(6));
                qString = replaceQueryVars(qString, "{{ra.upper}}", bounds[1].getRa().toFixed(6));
                qString = replaceQueryVars(qString, "{{dec.lower}}", bounds[1].getDec().toFixed(6));
                qString = replaceQueryVars(qString, "{{dec.upper}}", bounds[0].getDec().toFixed(6));
            }
            setQueryString(qString);
        }
            
    }

    function replaceQueryVars(str, replacee, replacer) {
        return str.replace(replacee, replacer);
    }
    
    function getVPBounds() {
        // get current VP bounds
        // bounds is array of CelestialCoordinates: [topLeft, bottomRight]
        return  di.readVariable(UW.astro.DEFAULT_NAMESPACE, "Viewport_Current_Bounds");
 
    }
    
    function makeGETRequest(url, data, callback) {
        var params = {};
        getdata = gadgets.io.encodeValues(data);
        params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
        url = url + '?' + getdata;
        gadgets.io.makeRequest(url, callback, params); 
    }
    
    function queryResponseCB(obj) {
        // parse the XML
        if (obj.errors.length > 0){
            // errors
            debugLog(obj.errors);
            if (obj.errors[0].indexOf("504") !== -1) {
                showError("Request Timed out.<br/>Try restricting search to<br/>smaller ra/dec bounds.");
            } else {
                showError('Service: ' + obj.errors);
            }
            return;
        }
        else if (obj.text === ""){
            // returned empty string
            showError('Service returned empty string');
            return;
        }
        
        xmlDoc = xmlStringToDoc(obj.text);
        // walk XML to create points and write to objectData
        var rows = xmlDoc.getElementsByTagName('Row');
        if ((rows.length === 0) || ((rows.length === 1) && (rows[0].textContent.indexOf('No rows returned') !== -1))){
            // the special case answer is that no rows were returned
            showError('No objects from ' + getSelectedOption("selCatalog") + ' catalog');
            return;
        }
        
        // indicate status to user
        showSuccess("Returned " + rows.length + " records.");
        
        // We probably have valid data at this point, so parse it and display it
        // create and write Dataset from the row data
        var ds = createDataset(rows);
        ds.setName(getDatasetNameText());
        writeDataset(ds);
    }
    
    function arrayContains(a, obj) {
        var i = a.length;
        while (i--) {
            if (a[i] === obj) {
                return true;
            }
        }
        return false;
    }
    
    function writeDataset(ds) {
        // TODO: this needs to be redone with di functions like: di.getDataset_List, di.addDataset
       
        // write dataset list
        var list = di.readVariable(UW.astro.DEFAULT_NAMESPACE, "Dataset_List");
        if (list === null) {
            list = [];
        }
        var name = ds.getName();
        if (!arrayContains(list, name)) { 
            di.registerVariable(name, null);
            di.writeVariable(UW.astro.DEFAULT_NAMESPACE, name, ds);
            list.push(name);
            di.writeVariable(UW.astro.DEFAULT_NAMESPACE, "Dataset_List", list);
        }
        else {
            // write dataset
            di.writeVariable(UW.astro.DEFAULT_NAMESPACE, name, ds);
        }
 
        
    }
        
    // Accepts "rows" as an ObjectHTML collection
    // and creates a Dataset from it and returns it
    function createDataset(rows) {
        var json = [];
        for (var i=0,len=rows.length; i<len; i++){
            var row = rows[i];
            var elem = {};
            // loop through attributes
            for (var k=0, kLen=row.attributes.length; k<kLen; k++){
                // k is the attribute index
                var attName = row.attributes[k].nodeName;
                var attValue = row.attributes[k].nodeValue;
                elem[attName] = attValue;
            }
            json.push(elem);
        }

        // create dataset
        ds = new UW.astro.Dataset(json);

        return ds;
    }

// HELPER
    function clearStatus() {
        showStatus(ICON.NONE, "");
    }
    function showSpinner(text) {
        showStatus(ICON.SPINNER, text);
    }
    function showSuccess(text) {
        showStatus(ICON.SUCCESS, text);
    }
    function showError(text) {
        showStatus(ICON.ERROR, text);
    }
     
    function showStatus(iconUrl, text){
        if (iconUrl !== ICON.NONE) {
            setStatus( '<img id="imgStatus" src="' + iconUrl + '" /> ' + text);
        } else {
            setStatus(text);
        }
    }
   
    function setStatus(text) {
        // set text status
        var statusDiv = document.getElementById('divStatusText');
        statusDiv.innerHTML = text;
        // make visible
        statusDiv.style.visibility = 'block';
    }
    function setVPBounds() {
        var bounds = getVPBounds();
        if (bounds !== null) {
            getField('spViewportBounds').innerHTML = "ra between " + bounds[0].getRa().toFixed(4) + " and " + bounds[1].getRa().toFixed(4) + " and<br/>dec between " + bounds[1].getDec().toFixed(4) + " and " + bounds[0].getDec().toFixed(4);
        }
    }
    function xmlStringToDoc(text){
        if (window.DOMParser){
            parser=new DOMParser();
            xmlDoc=parser.parseFromString(text,"text/xml");
        }
        else // Internet Explorer
        {
            xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async="false";
            xmlDoc.loadXML(text); 
        } 
        // TODO: test to verify its created
        return xmlDoc;
    }
    
   
    // HTML Field Getters/Setters
    function getField(field) {
        if (!(_myFields.hasOwnProperty(field)) || (_myFields[field] === null)) {
            _myFields[field] = document.getElementById(field);
        }
        return _myFields[field];
    }
    function getQueryString() {
        return getField("txtQueryString").value;
    }
    function setQueryString(str) {
        setTextBoxValue("txtQueryString", str);
    }
    function setSelectedCatalog(catalog) {
        selectValue("selCatalog", catalog);
    }
    function restrictVPBounds() {
        return getField('ckVPBounds').checked;
    }

    function setTextBoxValue(textBoxName, value) {
        textBox = document.getElementById(textBoxName);
        if (textBox !== null && textBox !== undefined) {
            textBox.value = value;
        }
    }

    // accepts name of select box
    // returns the value in the select box, or ""
    function getSelectedOption(select) {
        var selName = document.getElementById(select);
        if (selName.length > 0){
            return selName.options[selName.selectedIndex].value;
        } else {
            return "";
        }
    }
    
    function selectValue(selectName, value) {
        var sel = document.getElementById(selectName);
        if (sel === undefined || sel === null) {
            return;
        }
        for (var i=0, len=sel.options.length; i<len; i++) {
            if (sel.options[i].value === value) {
                sel.options[i].selected = true;
                return;
            }
        }
    }
    
    // Accepts the name of a select object, and an array of strings
    // replaces the options with the array of strings
    function setSelectOptions(selectName, options) {
        var select = document.getElementById(selectName);
        if (select === null) {
            return;
        }
        // save old selected option so maybe it can be reselected
        var oldOpt = getSelectedOption(selectName);
        // clear options
        for (var i=select.length-1; i>=0; i--) {
            select.remove(i);
        }
        // add options
        for (var i=0, len=options.length; i<len; i++) {
            // if oldOption is in the list, then select it
            select.add(new Option(options[i], options[i], false, (oldOpt === options[i])), null);
        }
    
    }
 

    return my;
}


    gadgets.util.registerOnLoadHandler(init); 


</script>
  
  ]]>
 </Content>
</Module>
