<commit_before>/*
 * Utils to extract the rendered kNetwork in KnetMaps (Network View), fetch back-end API summary/metadata and upload/save to knetspace via API.
 * Author: Ajit Singh
 */

/* Function to save knetwork rendered in knetmaps (cyJS) to knetspace via POST. */
 function exportAsJson(networkId, requestParams) {
   var cy= $('#cy').cytoscape('get'); // now we have a global reference to `cy`

   var exportJson= cy.json(); // full graphJSON

   var exportedJson= filterJsonToExport(cy, exportJson); // the final "graph" to export
   var thumbnail_image= exportThumbnail(cy); // fetch knetwork thumbnail as well.
   // fetch total node & edge count for this knetwork.
   var totalNodes= cy.$(':visible').nodes().size();
   var totalEdges= cy.$(':visible').edges().size();
   // formatted date: yyyy-mm-dd hh:mm (mm: January=0)
   var currentDate= new Date();
   var knet_date= currentDate.getFullYear() +'-'+ String(currentDate.getMonth() + 1).padStart(2, '0') +'-'+ String(currentDate.getDate()).padStart(2, '0')
           +' '+ currentDate.getHours() +':'+ ('0'+currentDate.getMinutes()).slice(-2);

   console.log("knet-save.js: networkId= "+ networkId); // test
   var knet_name= "myKnetwork.json", apiGraphSummary= null;
   if(networkId === null) { // for a new knetwork, fetch graphSummary from KnetMiner server API.
      console.log("fetch graphSummary from KnetMiner server API..."); // test
      apiGraphSummary= getGraphDBSummary();
     }

   // add api_graphSummary to the above as well, if exists.
   var speciesTaxid= null, speciesName= null, dbVersion= null, dbDateCreated= null, sourceOrganization= null, provider= null;
   if(apiGraphSummary !== null && apiGraphSummary.size > 0) {
      speciesTaxid= apiGraphSummary.get("speciesTaxid");
      speciesName= apiGraphSummary.get("speciesName");
      dbVersion= apiGraphSummary.get("dbVersion");
      dbDateCreated= apiGraphSummary.get("dbDateCreated");
      sourceOrganization= apiGraphSummary.get("sourceOrganization");
      provider= apiGraphSummary.get("provider");
      //console.log(speciesTaxid +","+ speciesName +","+ dbVersion +","+ dbDateCreated +","+ sourceOrganization +","+ provider); // test
     }

   // get keyword and gene list too (if found) to save.
   var keywords= null, gene_list= null;
   if(requestParams !== null) { // for a new knetwork, fetch graphSummary from KnetMiner server API.
      gene_list= JSON.stringify(requestParams.list);
      if(requestParams.keyword !== null) { keywords= requestParams.keyword; }
     // console.log("gene_list: "+ gene_list +", keywords: "+ keywords); // test
     }

   // default knetName & knetDesc for modal (jBox)
   var knetName= knet_name;
   var knetDesc= "Network for " + knetName;

   // POST to knetspace via /api/v1/networks/
  // var knetspace_api_host= "http://babvs72.rothamsted.ac.uk:8000"; //or "http://localhost:8000";
   var knetspace_api_host; // relative domain
   getKsAPI().then(function(ksAddress) {
       knetspace_api_host = ksAddress;
      // console.log("fetched knetspace_api_host: "+ knetspace_api_host); // test
    });
    isLoggedIn().then(function (bool) {
        if (bool) {
            if (networkId === null) {
                //if(typeof api_url !== "undefined") { // if it's within knetminer (DISABLED: as it breaks genepage api)
                userPlan().then(function (userBool) {
                    if (!userBool) {
                        // jBox modal to let users enter knetName & knetDesc and save when POSTing a new kNetwork.
                        var uploadHtml = "<form class='form' method='post' action='#'>"
                                + "Knetwork name" + "

"
                                + "<input style='height:20px;width:450px;' placeholder=" + knet_name + " type='text' name='knetName' id='kNetName'>" + "

"
                                + "Description" + "

"
                                + "<textarea style='height:200px;width:450px;' placeholder='Enter your description here...' name='knetDesc' id='knetDescription'></textarea>" + "

"
                                + "Private:"
                                + ""
                                + "<input type='checkbox' id='privateCheck'>"
                                + "" + "

"
                                + "<input type='button' name='KnetSubmit' id='KnetSubmit' value='Submit'>" + "</form>";
                    } else {
                            var uploadHtml = "<form class='form' method='post' action='#'>"
                                + "Knetwork name" + "

"
                                + "<input style='height:20px;width:450px;' placeholder=" + knet_name + " type='text' name='knetName' id='kNetName'>" + "

"
                                + "Description" + "

"
                                + "<textarea style='height:200px;width:450px;' placeholder='Enter your description here...' name='knetDesc' id='knetDescription'></textarea>" + "

"
                                + "Private: "
                                + ""
                                + "<input type='checkbox' id='privateCheck'>"
                                + "" + "

"
                                + "<input type='button' name='KnetSubmit' id='KnetSubmit' value='Submit'>" + "</form>";
                    }

                var uploadModal = new jBox('Modal', {
                    title: 'Save to KnetSpace',
                    animation: 'pulse', content: uploadHtml, width: 500, cancelButton: 'Exit', draggable: 'title',
                    attributes: {x: 'right', y: 'top'}, delayOpen: 50
                });
                uploadModal.open(); // open

                $('#KnetSubmit').on('click', function () {
                    //console.log("user inputs: "+ $('input[name=knetName]').val() +", "+ $('textarea#knetDescription').val());
                    if ($('input[name=knetName]').val()) {
                        knetName = $('input[name=knetName]').val();
                    } else {
                        knetName = knet_name;
                    }

                    if ($('textarea#knetDescription').val()) {
                        knetDesc = $.trim($('textarea#knetDescription').val());
                    } else {
                        knetDesc = "Network for " + knetName;
                    }
                    if (!userBool) {
                        var privateBool = document.getElementById('privateCheck').checked;
                        if(privateBool === true) {privateBool = false;} else {privateBool = true;}
                    } else {
                        var privateBool = true;
                    }
                    console.log("Public Knetwork? : " + privateBool);
                    // console.log("from user: knetName: "+ knetName + ", desc: "+ knetDesc); // test

                    // POST a new knetwork to knetspace with name, date_created, apiGraphSummary fields plus this graph, image, numNodes, numEdges.
                    var post_json = JSON.stringify({name: knetName, dateCreated: knet_date, numNodes: totalNodes, numEdges: totalEdges, graph: JSON.parse(exportedJson),
                        image: thumbnail_image, speciesTaxid: speciesTaxid, speciesName: species_name, dbVersion: dbVersion, dbDateCreated: dbDateCreated,
                        sourceOrganization: sourceOrganization, provider: provider, description: knetDesc, gene: gene_list, keyword: keywords, is_public: privateBool });
                    if (keywords === null || keywords === "") { // for non-keyword search
                        post_json = JSON.stringify({name: knetName, dateCreated: knet_date, numNodes: totalNodes, numEdges: totalEdges, graph: JSON.parse(exportedJson),
                            image: thumbnail_image, speciesTaxid: speciesTaxid, speciesName: species_name, dbVersion: dbVersion, dbDateCreated: dbDateCreated,
                            sourceOrganization: sourceOrganization, provider: provider, description: knetDesc, gene: gene_list, is_public: privateBool});
                    }

                    $.ajax({
                        type: 'POST',
                        url: knetspace_api_host + '/api/v1/networks/',
                        timeout: 1000000,
                        xhrFields: {withCredentials: true},
                        headers: {
                            "Accept": "application/json; charset=utf-8",
                            "Content-Type": "application/json; charset=utf-8"
                        },
                        datatype: "json",
                        data: post_json,
                        beforeSend: function() {
                            jboxNotice("Saving to KnetSpace...", 'blue', 60, 1000);
                        }
                    }).fail(function (errorlog) {
                        $('#overlay').fadeOut();
                        if(JSON.stringify(errorlog.responseText).includes("quota")) {
                            uploadModal.toggle();
                            var failedMsgQ = "You've exceeded your KnetSpace Knetwork quota!

" + "Click here to buy more Knetworks.";
                            jboxNotice(failedMsgQ, 'red', 60, 15000);
                        } else {
                            uploadModal.toggle();
                            var failedMsg = "Failed to upload kNetwork " + knetName;
                            jboxNotice(failedMsg, 'red', 60, 2500);
                            console.log("POST error: " + JSON.stringify(errorlog));
                        }
                    }).success(function (data) {
                        $('#overlay').fadeOut();
                        uploadModal.toggle();
                        var netContentSuccess = "Successfully saved!

" + "View it in KnetSpace";
                        jboxNotice(netContentSuccess, 'blue', 60, 15000);
                    });
                    // jBox modal - tasks completed.
                    uploadModal.destroy(); // destroy jBox modal when done
                });
            });
                //}
            } else { // PATCH existing networkId with updated graph, image, numNodes, numEdges, dateModified.
                var patch_json = JSON.stringify({dateModified: knet_date, numNodes: totalNodes, numEdges: totalEdges,
                    graph: JSON.parse(exportedJson), image: thumbnail_image});
                $.ajax({
                    type: 'PATCH',
                    url: knetspace_api_host + '/api/v1/networks/' + networkId + '/',
                    timeout: 1000000,
                    xhrFields: {withCredentials: true},
                    headers: {
                        "Accept": "application/json; charset=utf-8",
                        "Content-Type": "application/json; charset=utf-8"
                    },
                    datatype: "json",
                    data: patch_json,
                    beforeSend: function () {
                        jboxNotice("Sending Knetwork to KnetSpace...", 'blue', 60, 1000);
                    }
                })
                        .fail(function (errorlog) {
                            $('#overlay').fadeOut();
                            uploadModal.toggle();
                            var failedMsg = "Failed to upload kNetwork " + knetName;
                            jboxNotice(failedMsg, 'red', 60, 1000);
                            console.log("PATCH error: " + JSON.stringify(errorlog));
                        })
                        .success(function (data) {
                            $('#overlay').fadeOut();
                            uploadModal.toggle();
                            var netContentSuccess = "Network " + knetName + " submitted!

" + "View it in KnetSpace";
                            jboxNotice(netContentSuccess, 'blue', 60, 15000);
                            console.log("PATCH response: " + data);
                        });
            }

        } else {
            //jboxNotice("You're not logged in! Please log in!", 'red', 60, 1000)
            $('#loginBox').remove();
            loginModalInit();
        }
    });
};

 // generate pure JSON to export from KnetMaps for graphJSON and metadata
 function filterJsonToExport(cy, exportJson) {
   var elesToRetain= []; // to streamline content exported in exportJson (graphJSON) & metaJSON (allGraphData).
   cy.$(':visible').forEach(function (ele) { elesToRetain.push(ele.id()); });

   // remove hidden nodes/edges metadata from exportJson.
   exportJson.elements.nodes= exportJson.elements.nodes.filter( function( con ) {
	   return elesToRetain.includes(con.data.id); });
   exportJson.elements.edges= exportJson.elements.edges.filter( function( rel ) {
	   return elesToRetain.includes(rel.data.id); });

   // remove hidden nodes/edges metadata from metaJSON (allGraphData).
   var metaJSON= allGraphData; // differs from knetspace/knetmaps
   metaJSON.ondexmetadata.concepts= metaJSON.ondexmetadata.concepts.filter( function( con ) {
	   return elesToRetain.includes(con.id); });
   metaJSON.ondexmetadata.relations= metaJSON.ondexmetadata.relations.filter( function( rel ) {
	   return elesToRetain.includes(rel.id); });
   // remove other redundant metaJSON entries.
   var omit_redundant= ["graphName","numberOfConcepts","numberOfRelations","version"];
   omit_redundant.forEach(function (entry) { delete metaJSON.ondexmetadata[entry]; });

   var json_response= '{"graphJSON":'+ JSON.stringify(exportJson) + ', "allGraphData":' + JSON.stringify(metaJSON) +'}';
   return json_response;
  }

 // fetch graphSummary from KnetMiner server API.
 function getGraphDBSummary() {
   var graphSummary= new Map();
   if(typeof api_url !== "undefined") {
        $.ajax({
            async: false,
            type: 'GET',
            url: api_url + '/dataSource',
            success: function (data) {
                var api_response= data.dataSource.replace(/\"/g,"").trim().split(",");
                api_response.forEach(function(val) {
                    var values= val.split(":");
                    var k= values[0].trim();
                    var v= values[1].trim();
                    if(k === "dbDateCreated") { v= values[1] +":"+ values[2]; }
                    graphSummary.set(k, v);
                });
           }
       });
     }
    return graphSummary;
  }

  // Export the network thumbnail.
  function exportThumbnail(cy) {
   var png64 = cy.png({ "scale" : 2.0 /*0.8*/, "output" : 'base64'}); // .setAttribute('crossOrigin', 'anonymous');

   return png64.replace("data:image/png;base64,", "");
  }
<commit_msg>knetName fixed again<commit_after>/*
 * Utils to extract the rendered kNetwork in KnetMaps (Network View), fetch back-end API summary/metadata and upload/save to knetspace via API.
 * Author: Ajit Singh
 */

/* Function to save knetwork rendered in knetmaps (cyJS) to knetspace via POST. */
 function exportAsJson(networkId, requestParams) {
   var cy= $('#cy').cytoscape('get'); // now we have a global reference to `cy`

   var exportJson= cy.json(); // full graphJSON

   var exportedJson= filterJsonToExport(cy, exportJson); // the final "graph" to export
   var thumbnail_image= exportThumbnail(cy); // fetch knetwork thumbnail as well.
   // fetch total node & edge count for this knetwork.
   var totalNodes= cy.$(':visible').nodes().size();
   var totalEdges= cy.$(':visible').edges().size();
   // formatted date: yyyy-mm-dd hh:mm (mm: January=0)
   var currentDate= new Date();
   var knet_date= currentDate.getFullYear() +'-'+ String(currentDate.getMonth() + 1).padStart(2, '0') +'-'+ String(currentDate.getDate()).padStart(2, '0')
           +' '+ currentDate.getHours() +':'+ ('0'+currentDate.getMinutes()).slice(-2);

   console.log("knet-save.js: networkId= "+ networkId); // test
   var knet_name= "my knetwork", apiGraphSummary= null;
   if(networkId === null) { // for a new knetwork, fetch graphSummary from KnetMiner server API.
      console.log("fetch graphSummary from KnetMiner server API..."); // test
      apiGraphSummary= getGraphDBSummary();
     }

   // add api_graphSummary to the above as well, if exists.
   var speciesTaxid= null, speciesName= null, dbVersion= null, dbDateCreated= null, sourceOrganization= null, provider= null;
   if(apiGraphSummary !== null && apiGraphSummary.size > 0) {
      speciesTaxid= apiGraphSummary.get("speciesTaxid");
      speciesName= apiGraphSummary.get("speciesName");
      dbVersion= apiGraphSummary.get("dbVersion");
      dbDateCreated= apiGraphSummary.get("dbDateCreated");
      sourceOrganization= apiGraphSummary.get("sourceOrganization");
      provider= apiGraphSummary.get("provider");
      //console.log(speciesTaxid +","+ speciesName +","+ dbVersion +","+ dbDateCreated +","+ sourceOrganization +","+ provider); // test
     }

   // get keyword and gene list too (if found) to save.
   var keywords= null, gene_list= null;
   if(requestParams !== null) { // for a new knetwork, fetch graphSummary from KnetMiner server API.
      gene_list= JSON.stringify(requestParams.list);
      if(requestParams.keyword !== null) { keywords= requestParams.keyword; }
     // console.log("gene_list: "+ gene_list +", keywords: "+ keywords); // test
     }

   // default knetName & knetDesc for modal (jBox)
   var knetName= knet_name;
   var knetDesc= knetName;

   // POST to knetspace via /api/v1/networks/
  // var knetspace_api_host= "http://babvs72.rothamsted.ac.uk:8000"; //or "http://localhost:8000";
   var knetspace_api_host; // relative domain
   getKsAPI().then(function(ksAddress) {
       knetspace_api_host = ksAddress;
      // console.log("fetched knetspace_api_host: "+ knetspace_api_host); // test
    });
    isLoggedIn().then(function (bool) {
        if (bool) {
            if (networkId === null) {
                //if(typeof api_url !== "undefined") { // if it's within knetminer (DISABLED: as it breaks genepage api)
                userPlan().then(function (userBool) {
                    if (!userBool) {
                        // jBox modal to let users enter knetName & knetDesc and save when POSTing a new kNetwork.
                        var uploadHtml = "<form class='form' method='post' action='#'>"
                                + "Knetwork name" + "

"
                                + "<input style='height:20px;width:450px;' placeholder=" + knet_name + " type='text' name='knetName' id='kNetName'>" + "

"
                                + "Description" + "

"
                                + "<textarea style='height:200px;width:450px;' placeholder='Enter your description here...' name='knetDesc' id='knetDescription'></textarea>" + "

"
                                + "Private:"
                                + ""
                                + "<input type='checkbox' id='privateCheck'>"
                                + "" + "

"
                                + "<input type='button' name='KnetSubmit' id='KnetSubmit' value='Submit'>" + "</form>";
                    } else {
                            var uploadHtml = "<form class='form' method='post' action='#'>"
                                + "Knetwork name" + "

"
                                + "<input style='height:20px;width:450px;' placeholder=" + knet_name + " type='text' name='knetName' id='kNetName'>" + "

"
                                + "Description" + "

"
                                + "<textarea style='height:200px;width:450px;' placeholder='Enter your description here...' name='knetDesc' id='knetDescription'></textarea>" + "

"
                                + "Private: "
                                + ""
                                + "<input type='checkbox' id='privateCheck'>"
                                + "" + "

"
                                + "<input type='button' name='KnetSubmit' id='KnetSubmit' value='Submit'>" + "</form>";
                    }

                var uploadModal = new jBox('Modal', {
                    title: 'Save to KnetSpace',
                    animation: 'pulse', content: uploadHtml, width: 500, cancelButton: 'Exit', draggable: 'title',
                    attributes: {x: 'right', y: 'top'}, delayOpen: 50
                });
                uploadModal.open(); // open

                $('#KnetSubmit').on('click', function () {
                    //console.log("user inputs: "+ $('input[name=knetName]').val() +", "+ $('textarea#knetDescription').val());
                    if ($('input[name=knetName]').val()) {
                        knetName = $('input[name=knetName]').val();
                    } else {
                        knetName = knet_name;
                    }

                    if ($('textarea#knetDescription').val()) {
                        knetDesc = $.trim($('textarea#knetDescription').val());
                    } else {
                        knetDesc = "Network for " + knetName;
                    }
                    if (!userBool) {
                        var privateBool = document.getElementById('privateCheck').checked;
                        if(privateBool === true) {privateBool = false;} else {privateBool = true;}
                    } else {
                        var privateBool = true;
                    }
                    console.log("Public Knetwork? : " + privateBool);
                    // console.log("from user: knetName: "+ knetName + ", desc: "+ knetDesc); // test

                    // POST a new knetwork to knetspace with name, date_created, apiGraphSummary fields plus this graph, image, numNodes, numEdges.
                    var post_json = JSON.stringify({name: knetName, dateCreated: knet_date, numNodes: totalNodes, numEdges: totalEdges, graph: JSON.parse(exportedJson),
                        image: thumbnail_image, speciesTaxid: speciesTaxid, speciesName: species_name, dbVersion: dbVersion, dbDateCreated: dbDateCreated,
                        sourceOrganization: sourceOrganization, provider: provider, description: knetDesc, gene: gene_list, keyword: keywords, is_public: privateBool });
                    if (keywords === null || keywords === "") { // for non-keyword search
                        post_json = JSON.stringify({name: knetName, dateCreated: knet_date, numNodes: totalNodes, numEdges: totalEdges, graph: JSON.parse(exportedJson),
                            image: thumbnail_image, speciesTaxid: speciesTaxid, speciesName: species_name, dbVersion: dbVersion, dbDateCreated: dbDateCreated,
                            sourceOrganization: sourceOrganization, provider: provider, description: knetDesc, gene: gene_list, is_public: privateBool});
                    }

                    $.ajax({
                        type: 'POST',
                        url: knetspace_api_host + '/api/v1/networks/',
                        timeout: 1000000,
                        xhrFields: {withCredentials: true},
                        headers: {
                            "Accept": "application/json; charset=utf-8",
                            "Content-Type": "application/json; charset=utf-8"
                        },
                        datatype: "json",
                        data: post_json,
                        beforeSend: function() {
                            jboxNotice("Saving to KnetSpace...", 'blue', 60, 1000);
                        }
                    }).fail(function (errorlog) {
                        $('#overlay').fadeOut();
                        if(JSON.stringify(errorlog.responseText).includes("quota")) {
                            uploadModal.toggle();
                            var failedMsgQ = "You've exceeded your KnetSpace Knetwork quota!

" + "Click here to buy more Knetworks.";
                            jboxNotice(failedMsgQ, 'red', 60, 15000);
                        } else {
                            uploadModal.toggle();
                            var failedMsg = "Failed to upload kNetwork " + knetName;
                            jboxNotice(failedMsg, 'red', 60, 2500);
                            console.log("POST error: " + JSON.stringify(errorlog));
                        }
                    }).success(function (data) {
                        $('#overlay').fadeOut();
                        uploadModal.toggle();
                        var netContentSuccess = "Successfully saved!

" + "View it in KnetSpace";
                        jboxNotice(netContentSuccess, 'blue', 60, 15000);
                    });
                    // jBox modal - tasks completed.
                    uploadModal.destroy(); // destroy jBox modal when done
                });
            });
                //}
            } else { // PATCH existing networkId with updated graph, image, numNodes, numEdges, dateModified.
                var patch_json = JSON.stringify({dateModified: knet_date, numNodes: totalNodes, numEdges: totalEdges,
                    graph: JSON.parse(exportedJson), image: thumbnail_image});
                $.ajax({
                    type: 'PATCH',
                    url: knetspace_api_host + '/api/v1/networks/' + networkId + '/',
                    timeout: 1000000,
                    xhrFields: {withCredentials: true},
                    headers: {
                        "Accept": "application/json; charset=utf-8",
                        "Content-Type": "application/json; charset=utf-8"
                    },
                    datatype: "json",
                    data: patch_json,
                    beforeSend: function () {
                        jboxNotice("Sending Knetwork to KnetSpace...", 'blue', 60, 1000);
                    }
                })
                        .fail(function (errorlog) {
                            $('#overlay').fadeOut();
                            uploadModal.toggle();
                            var failedMsg = "Failed to upload kNetwork " + knetName;
                            jboxNotice(failedMsg, 'red', 60, 1000);
                            console.log("PATCH error: " + JSON.stringify(errorlog));
                        })
                        .success(function (data) {
                            $('#overlay').fadeOut();
                            uploadModal.toggle();
                            var netContentSuccess = "Network " + knetName + " submitted!

" + "View it in KnetSpace";
                            jboxNotice(netContentSuccess, 'blue', 60, 15000);
                            console.log("PATCH response: " + data);
                        });
            }

        } else {
            //jboxNotice("You're not logged in! Please log in!", 'red', 60, 1000)
            $('#loginBox').remove();
            loginModalInit();
        }
    });
};

 // generate pure JSON to export from KnetMaps for graphJSON and metadata
 function filterJsonToExport(cy, exportJson) {
   var elesToRetain= []; // to streamline content exported in exportJson (graphJSON) & metaJSON (allGraphData).
   cy.$(':visible').forEach(function (ele) { elesToRetain.push(ele.id()); });

   // remove hidden nodes/edges metadata from exportJson.
   exportJson.elements.nodes= exportJson.elements.nodes.filter( function( con ) {
	   return elesToRetain.includes(con.data.id); });
   exportJson.elements.edges= exportJson.elements.edges.filter( function( rel ) {
	   return elesToRetain.includes(rel.data.id); });

   // remove hidden nodes/edges metadata from metaJSON (allGraphData).
   var metaJSON= allGraphData; // differs from knetspace/knetmaps
   metaJSON.ondexmetadata.concepts= metaJSON.ondexmetadata.concepts.filter( function( con ) {
	   return elesToRetain.includes(con.id); });
   metaJSON.ondexmetadata.relations= metaJSON.ondexmetadata.relations.filter( function( rel ) {
	   return elesToRetain.includes(rel.id); });
   // remove other redundant metaJSON entries.
   var omit_redundant= ["graphName","numberOfConcepts","numberOfRelations","version"];
   omit_redundant.forEach(function (entry) { delete metaJSON.ondexmetadata[entry]; });

   var json_response= '{"graphJSON":'+ JSON.stringify(exportJson) + ', "allGraphData":' + JSON.stringify(metaJSON) +'}';
   return json_response;
  }

 // fetch graphSummary from KnetMiner server API.
 function getGraphDBSummary() {
   var graphSummary= new Map();
   if(typeof api_url !== "undefined") {
        $.ajax({
            async: false,
            type: 'GET',
            url: api_url + '/dataSource',
            success: function (data) {
                var api_response= data.dataSource.replace(/\"/g,"").trim().split(",");
                api_response.forEach(function(val) {
                    var values= val.split(":");
                    var k= values[0].trim();
                    var v= values[1].trim();
                    if(k === "dbDateCreated") { v= values[1] +":"+ values[2]; }
                    graphSummary.set(k, v);
                });
           }
       });
     }
    return graphSummary;
  }

  // Export the network thumbnail.
  function exportThumbnail(cy) {
   var png64 = cy.png({ "scale" : 2.0 /*0.8*/, "output" : 'base64'}); // .setAttribute('crossOrigin', 'anonymous');

   return png64.replace("data:image/png;base64,", "");
  }
<|endoftext|>


<commit_before>/*globals Storage, Query, ObjectId, Panel*/
/**
 * @file Manage populate operations
 */
'use strict'

var Populate = {}

/**
 * Registered populate operations
 * @property {Storage}
 */
Populate.paths = new Storage('populate')

/**
 * Create and run a populate operation
 * @param {string} conn
 * @param {string} coll
 * @param {string} path
 * @param {string} targetConn
 * @param {string} targetColl
 * @param {string} targetPath
 */
Populate.create = function (conn, coll, path, targetConn, targetColl, targetPath) {
	var op = {
		path: path,
		targetConn: targetConn,
		targetColl: targetColl,
		targetPath: targetPath
	}
	Populate.paths.getArray(conn, coll).pushAndSave(op)
	Populate.run(op)
}

/**
 * Remove a populate operation
 * @param {string} conn
 * @param {string} coll
 * @param {string} path
 */
Populate.remove = function (conn, coll, path) {
	var ops = Populate.paths.getArray(conn, coll)
	ops.set(ops.filter(function (op) {
		return op.path !== path && path.indexOf(op.path + '.') !== 0
	}))
	// TODO: find a better way of doing this
	window.location.reload()
}

/**
 * Run all operations related to a collection
 * @param {string} conn
 * @param {string} coll
 */
Populate.runAll = function (conn, coll) {
	Populate.paths.getArray(conn, coll).forEach(Populate.run)
}

/**
 * Return all paths that are/will be populate
 * @param {string} conn
 * @param {string} coll
 * @returns {Array<string>}
 */
Populate.getPaths = function (conn, coll) {
	return Populate.paths.getArray(conn, coll).map(function (op) {
		return op.path
	})
}

/**
 * Check whether a given path is populated
 * @param {Array<string>} paths - populated paths (returned by getPaths)
 * @param {string} path - path to check
 * @returns {boolean}
 */
Populate.isPopulated = function (paths, path) {
	return paths.some(function (each) {
		return each === path || path.indexOf(each + '.') === 0
	})
}

/**
 * Run a given populate operation
 * @param {Object} op
 */
Populate.run = function (op) {
	var replaceSites = Object.create(null),
		pathParts = op.path.split('.'),
		ids

	// Collect ids to replace
	Query.result.forEach(function (doc) {
		var i, parent
		for (i = 0; i < pathParts.length; i++) {
			if (!doc ||
				typeof doc !== 'object' ||
				Array.isArray(doc) ||
				Query.specialTypes.indexOf(doc.constructor) !== -1) {
				return
			}
			parent = doc
			doc = doc[pathParts[i]]
		}

		if (doc instanceof ObjectId) {
			if (!(doc in replaceSites)) {
				replaceSites[doc] = []
			}
			replaceSites[doc].push(parent)
		}
	})
	ids = Object.keys(replaceSites)

	// Find in the DB
	if (!ids.length) {
		return
	}
	Panel.request('populate', {
		connection: op.targetConn,
		collection: op.targetColl,
		ids: ids,
		path: op.targetPath
	}, function (result) {
		var path = pathParts[pathParts.length - 1]
		result.docs.forEach(function (doc) {
			replaceSites[doc._id].forEach(function (each) {
				each[path] = op.targetPath ? doc[op.targetPath] : doc
			})
		})
		Query.populateResultTable()
	})
}<commit_msg>Fix to populate<commit_after>/*globals Storage, Query, ObjectId, Panel*/
/**
 * @file Manage populate operations
 */
'use strict'

var Populate = {}

/**
 * Registered populate operations
 * @property {Storage}
 */
Populate.paths = new Storage('populate')

/**
 * Create and run a populate operation
 * @param {string} conn
 * @param {string} coll
 * @param {string} path
 * @param {string} targetConn
 * @param {string} targetColl
 * @param {string} targetPath
 */
Populate.create = function (conn, coll, path, targetConn, targetColl, targetPath) {
	var op = {
		path: path,
		targetConn: targetConn,
		targetColl: targetColl,
		targetPath: targetPath
	}
	Populate.paths.getArray(conn, coll).pushAndSave(op)
	Populate.run(op)
}

/**
 * Remove a populate operation
 * @param {string} conn
 * @param {string} coll
 * @param {string} path
 */
Populate.remove = function (conn, coll, path) {
	var ops = Populate.paths.getArray(conn, coll)
	ops.set(ops.filter(function (op) {
		return op.path !== path && path.indexOf(op.path + '.') !== 0
	}))
	// TODO: find a better way of doing this
	window.location.reload()
}

/**
 * Run all operations related to a collection
 * @param {string} conn
 * @param {string} coll
 */
Populate.runAll = function (conn, coll) {
	Populate.paths.getArray(conn, coll).forEach(Populate.run)
}

/**
 * Return all paths that are/will be populate
 * @param {string} conn
 * @param {string} coll
 * @returns {Array<string>}
 */
Populate.getPaths = function (conn, coll) {
	return Populate.paths.getArray(conn, coll).map(function (op) {
		return op.path
	})
}

/**
 * Check whether a given path is populated
 * @param {Array<string>} paths - populated paths (returned by getPaths)
 * @param {string} path - path to check
 * @returns {boolean}
 */
Populate.isPopulated = function (paths, path) {
	return paths.some(function (each) {
		return each === path || path.indexOf(each + '.') === 0
	})
}

/**
 * Run a given populate operation
 * @param {Object} op
 */
Populate.run = function (op) {
	var replaceSites = Object.create(null),
		pathParts = op.path.split('.'),
		ids

	// Collect ids to replace
	Query.result.forEach(function (doc) {
		var i, parent
		for (i = 0; i < pathParts.length; i++) {
			if (!doc ||
				typeof doc !== 'object' ||
				Array.isArray(doc) ||
				Query.specialTypes.indexOf(doc.constructor) !== -1) {
				return
			}
			parent = doc
			doc = doc[pathParts[i]]
		}

		if (doc instanceof ObjectId) {
			if (!(doc in replaceSites)) {
				replaceSites[doc] = []
			}
			replaceSites[doc].push(parent)
		}
	})
	ids = Object.keys(replaceSites)

	// Find in the DB
	if (!ids.length) {
		return
	}
	Panel.request('populate', {
		connection: op.targetConn,
		collection: op.targetColl,
		ids: ids,
		path: op.targetPath
	}, function (result) {
		var parentPath = pathParts[pathParts.length - 1],
			parts = op.targetPath.split('.')
		result.docs.forEach(function (doc) {
			replaceSites[doc._id].forEach(function (each) {
				each[parentPath] = op.targetPath ? getInPath(doc, parts) : doc
			})
		})
		Query.populateResultTable()
	})

	// return doc[path], but considering sub-docs
	function getInPath(doc, parts) {
		var i
		for (i = 0; i < parts.length; i++) {
			if (!doc ||
				typeof doc !== 'object' ||
				Array.isArray(doc) ||
				Query.specialTypes.indexOf(doc.constructor) !== -1) {
				return
			}
			doc = doc[parts[i]]
		}
		return doc
	}
}<|endoftext|>


<commit_before>// @flow
import type { Functor } from './Functor'
import type { Monad } from './Monad'
import type { HKT2 } from './HKT'

import { HKT } from './HKT'
import { Data1 } from './Data'

class IsFree {}

// we can think of a free monad as just being a list of functors

class Cons<F, A> extends Data1<HKT<F, Free<F, A>>> {}

class Nil<A> extends Data1<A> {}

export type FreeV<F, A> = Nil<A> | Cons<F, A>;

export type Free<F, A> = HKT2<IsFree, F, A>;

export function inj<F, A>(f: FreeV<F, A>): Free<F, A> {
  return ((f: any): Free<F, A>)
}

export function prj<F, A>(fa: Free<F, A>): FreeV<F, A> {
  return ((fa: any): FreeV<F, A>)
}

export function of<F, A>(a: A): Free<F, A> {
  return inj(new Nil(a))
}

export function cons<F, A>(ffa: HKT<F, Free<F, A>>): Free<F, A> {
  return inj(new Cons(ffa))
}

export function liftF<F, A>(functor: Functor<F>, fa: HKT<F, A>): Free<F, A> {
  return cons(functor.map(of, fa))
}

export function freeMonad<F>(functor: Functor<F>): Monad<HKT<IsFree, F>> {

  function map<A, B>(f: (a: A) => B, fa: Free<F, A>): Free<F, B> {
    const a = prj(fa)
    if (a instanceof Nil) {
      return of(f(a.value0))
    }
    return cons(functor.map(x => map(f, x), a.value0))
  }

  function ap<A, B>(fab: Free<F, (a: A) => B>, fa: Free<F, A>): Free<F, B> {
    return chain(f => map(f, fa), fab) // <= derived
  }

  function join<A>(ffa: Free<F, Free<F, A>>): Free<F, A> {
    const fa = prj(ffa)
    if (fa instanceof Nil) {
      return fa.value0
    }
    return cons(functor.map(join, fa.value0))
  }

  function chain<A, B>(f: (a: A) => Free<F, B>, fa: Free<F, A>): Free<F, B> {
    return join(map(f, fa)) // <= derived
  }

  return {
    map,
    ap,
    of,
    chain
  }

}
<commit_msg>foldFree (+ rename liftF to liftFree)<commit_after>// @flow
import type { Functor } from './Functor'
import type { Monad } from './Monad'
import type { HKT2 } from './HKT'

import { HKT } from './HKT'
import { Data1 } from './Data'

class IsFree {}

// we can think of a free monad as just being a list of functors

class Cons<F, A> extends Data1<HKT<F, Free<F, A>>> {}

class Nil<A> extends Data1<A> {}

export type FreeV<F, A> = Nil<A> | Cons<F, A>;

export type Free<F, A> = HKT2<IsFree, F, A>;

export function inj<F, A>(f: FreeV<F, A>): Free<F, A> {
  return ((f: any): Free<F, A>)
}

export function prj<F, A>(fa: Free<F, A>): FreeV<F, A> {
  return ((fa: any): FreeV<F, A>)
}

export function of<F, A>(a: A): Free<F, A> {
  return inj(new Nil(a))
}

export function cons<F, A>(ffa: HKT<F, Free<F, A>>): Free<F, A> {
  return inj(new Cons(ffa))
}

export function liftFree<F, A>(functor: Functor<F>, fa: HKT<F, A>): Free<F, A> {
  return cons(functor.map(of, fa))
}

export function foldFree<F, A>(functor: Functor<F>, join: (fa: HKT<F, A>) => A, ffa: Free<F, A>): A {
  const fa = prj(ffa)
  if (fa instanceof Nil) {
    return fa.value0
  }
  return join(functor.map(x => foldFree(functor, join, x), fa.value0))
}

export function freeMonad<F>(functor: Functor<F>): Monad<HKT<IsFree, F>> {

  function map<A, B>(f: (a: A) => B, fa: Free<F, A>): Free<F, B> {
    const a = prj(fa)
    if (a instanceof Nil) {
      return of(f(a.value0))
    }
    return cons(functor.map(x => map(f, x), a.value0))
  }

  function ap<A, B>(fab: Free<F, (a: A) => B>, fa: Free<F, A>): Free<F, B> {
    return chain(f => map(f, fa), fab) // <= derived
  }

  function join<A>(ffa: Free<F, Free<F, A>>): Free<F, A> {
    const fa = prj(ffa)
    if (fa instanceof Nil) {
      return fa.value0
    }
    return cons(functor.map(join, fa.value0))
  }

  function chain<A, B>(f: (a: A) => Free<F, B>, fa: Free<F, A>): Free<F, B> {
    return join(map(f, fa)) // <= derived
  }

  return {
    map,
    ap,
    of,
    chain
  }

}
<|endoftext|>


<commit_before>"use strict";
if (process.env.NODE_ENV === "development") {
    require("ts-node").register({
        project: require("path").join(__dirname, "../tsconfig.electron.json")
    });
    require("../main-src/electron");
} else {
    require("./main-src/electron");
}
<commit_msg>fix(electron): fix dev<commit_after>"use strict";
if (!!process.env.FAAO_URL) {
    require("ts-node").register({
        project: require("path").join(__dirname, "../tsconfig.electron.json")
    });
    require("../main-src/electron");
} else {
    require("./main-src/electron");
}
<|endoftext|>


<commit_before>
(function() {

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({
      classLabel: 'cdb.admin.TableView',
      events: cdb.core.View.extendEvents({
          'click .clearview': '_clearView',
          'click .sqlview .export_query': '_tableFromQuery',
          'click .noRows': 'addEmptyRow'
      }),

      rowView: cdb.admin.RowView,

      initialize: function() {
        var self = this;
        this.elder('initialize');
        this.options.row_header = true;
        this.model.data().bind('newPage', this.newPage, this);
        _.bindAll(this, "render", "addEmptyRow", "_checkEmptyTable", "_forceScroll", "rowChanged", "rowSynched", "rowFailed", "rowDestroyed", "emptyTable");
        var topReached = false;
        this._editorsOpened = null;

         // pagination
        setInterval(function() {
          if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
            return;
          }
          var pos = $(this).scrollTop();
          var d = self.model.data();
          // do not let to fetch previous pages
          // until the user dont scroll back a little bit
          if(pos > 2) {
            topReached = false;
          }
          var pageSize = $(window).height() - self.$el.offset().top;
          var tableHeight = this.$('tbody').height();
          var realPos = pos + pageSize;
          if(tableHeight < pageSize) {
            return;
          }
          if(realPos > tableHeight) {
             d.setPage(d.getPage() + 1);
          } else if (pos <= 0) {
            if(!topReached) {
              d.setPage(d.getPage() - 1);
            }
            topReached = true;
          }

        }, 2000);

        // Moving header and loaders when scrolls
        this.scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop(), last: 'vertical' };

        $(window).scroll(function(ev){
          // console.log(ev);
          var actual_scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop() };

          if (self.scroll_position.x != actual_scroll_position.x) {
            self.scroll_position.x = actual_scroll_position.x;
            self.$el.find("thead").addClass("horizontal");

            // If last change was vertical
            if (self.scroll_position.last == "vertical") {
              self.scroll_position.x = actual_scroll_position.x;
              self.$el.find("thead > tr > th > div").css({"marginLeft": "0px"})
              self.$el.find("thead div > div").css("top", actual_scroll_position.y + "px");
              self.scroll_position.last = "horizontal";
            }

          } else if (self.scroll_position.y != actual_scroll_position.y) {
            self.scroll_position.y = actual_scroll_position.y;
            self.$el.find("thead").removeClass("horizontal");

            // If last change was vertical
            if (self.scroll_position.last == "horizontal") {
              self.$el.find("thead > tr > th > div")
                .css({"marginLeft": "-" + actual_scroll_position.x + "px"})
              self.$el.find("thead > tr > th > div > div").removeAttr('style')
              self.scroll_position.last = "vertical";
            }
          }

          // move header
          // var actual = new Date().getTime();
          // if ((actual - self.timestamp) >= 250) {
          //   self.$el.find("thead > tr > th > div").css({marginLeft: -$(window).scrollLeft() + "px"});
          // }
        });

        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$el[fn]("

Loading more rows

Now viewing 120 of 264

");
        });

        this.model.data().bind('endLoadingRows', function() {
          //self.$el.find('.page_loader').remove();
        });

        this.bind('cellDblClick', this._editCell, this);
        this.model.bind('change:dataSource', this._onSQLView, this);
        // when model changes the header is re rendered so the notice should be added
        this.model.bind('change', this._onSQLView, this);
        this.model.bind('dataLoaded', function() {
          self._checkEmptyTable();
          self._forceScroll();
        });

        this.bind('createRow', function() {
          self._checkEmptyTable();
        });

        // this.model.bind('loadModelStarted', function() {
        //   self.$el.fadeOut()
        // })

        // this.model.bind('loadModelCompleted', function() {
        //   self.$el.fadeIn()
        // })

        // this.model.bind('loadModelFailed', function() {
        //   self.$el.fadeIn()
        // })

      },

      render: function(args) {
        this.elder('render', args);
        this.trigger('render');
      },

      _onSQLView: function() {
        // if we are in a SQL view add a header
        this.$('.sqlview').remove();
        if(this.model.isInSQLView()) {
          var empty = this.isEmptyTable();

          this.$('thead').append(
            this.getTemplate('table/views/sql_view_notice')({ empty: empty })
          );

          this.$('thead > tr').css('height', 64 + 42);
          if(this.isEmptyTable()) {
            this.addEmptySQLIfo();
          }
        }
      },

      _clearView: function(e) {
        e.preventDefault();
        this.trigger('clearSQLView');
        //this.model.useSQLView(null);
        return false;
      },

      _tableFromQuery: function(e) {
        var self = this;
        e.preventDefault();
        //this.model.useSQLView(null);
        var dlg = new cdb.admin.TableNameDialog({
          ok: function(name) {
            var sql = self.model.data().getSQL();
            alert('to be done in backend');
            /*
            var table = cdb.admin.CartoDBTableMetadata.createFromQuery(name, sql);

            table.save(null, {
              success: function() {
              },
              error: function() {
                cdb.log.error("problem creating table from query");
              }
            });
            */
          }
        });
        dlg.appendToBody().show();
        return false;
        /*
        from_query:SELECT * FROM cb_municipios_5000_e limit 1
        name:asdasdasd
        */
      },

      _getEditor: function(columnType, opts) {
        var editors = {
          'string': cdb.admin.EditStringDialog,
          'number': cdb.admin.EditNumberDialog,
          'date': cdb.admin.EditDateDialog,
          'geometry': cdb.admin.EditGeometryDialog,
          'boolean': cdb.admin.EditBooleanDialog
        };

        // clean previous if exits
        var view = this._editorsOpened;
        if(view) {
          view.clean();
        }

        var Editor = editors[columnType];
        this._editorsOpened = new Editor(opts);
        return this._editorsOpened;

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        if(this.model.data().isReadOnly()) {
          return;
        }
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var row = self.model.data().getRowAt(y);

        var initial_value = '';
        if(self.model.data().getCell(y, column) === 0 || self.model.data().getCell(y, column) === '0') {
          initial_value = '0';
        } else if (self.model.data().getCell(y, column)) {
          initial_value = self.model.data().getCell(y, column);
        }
        var dlg = this._getEditor(columnType, {
          initial_value: initial_value,
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        if(!dlg) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }

        // auto add to table view
        // Check first if the row is the first or the cell is the last :)
        var $td = $(e.target).closest("td")
          , offset = $td.position()
          , width = $td.outerWidth();

        if ($td.parent().index() == 0) {
          offset.top += 5;
        } else {
          offset.top -= 11;
        }

        if ($td.index() == ($td.size() - 1)) {
          offset.left -= 22;
        } else {
          offset.left -= 11;
        }

        dlg.showAt(offset.left, offset.top, width, true);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        var self = this;
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({
            column: column,
            table: this.model,
            sqlView: this.options.sqlView
          }).bind('georeference', function(column) {
              var dlg = new cdb.admin.GeoreferenceDialog({
                model: this.table,
                wizard_option: 1, /** geocode using a column */
                georeference_column: column,
                geocoder: self.options.geocoder
              });
              dlg.appendToBody().open();
          });

          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      },

      /**
      * Checks if the table has any rows, and if not, launch the method for showing the appropiate view elements
      * @method _checkEmptyTable
      */
      _checkEmptyTable: function() {
        if(this.isEmptyTable()) {
          this.addEmptyTableInfo();
        } else {
          this.cleanEmptyTableInfo();
        }
      },

      /**
      * Force the table to be at the beginning
      * @method _forceScroll
      */
      _forceScroll: function(ev){
        $(window).scrollLeft(0);
      },

      /**
      * Adds the view elements associated with no content in the table
      * @method addemptyTableInfo
      */
      addEmptyTableInfo: function() {
        if(this.$('.noRows').length == 0 && !this.model.isInSQLView()) {
          this.elder('addEmptyTableInfo');

          this.$el.hide();
          var columnsNumber = this.model.get('schema').length;
          var columns = '
+	';
          for(var i = 0; i < columnsNumber; i++) {
            columns += '	';
          }
          columns += '
';
          var columnsFooter = '
	';
          for(var i = 0; i < columnsNumber; i++) {
            columnsFooter += '	';
          }
          columnsFooter += '
';

          var $columns = $(columns+columnsFooter)
          this.$el.append($columns);

          this.template_base = cdb.templates.getTemplate('table/views/empty_table');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Adds the view elements associated with no content in the table when a SQL is applied
      * @method addEmptySQLIfo
      */
      addEmptySQLIfo: function() {
        if(this.model.isInSQLView()) {
          this.elder('addEmptySQLInfo');
          this.$('tbody').html('');
          this.$('tfoot').remove();
          this.$el.hide();

          this.template_base = cdb.templates.getTemplate('table/views/empty_sql');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Removes from the view the no-content elements
      * @method cleanEmptyTableInfo
      */
      cleanEmptyTableInfo: function() {
        this.$('tfoot').fadeOut('fast', function() {
          $(this).remove();
        })
        this.$('.noRows').slideUp('fast', function() {
            $(this).remove();
        })
      },

      /**
      * Add a new row and removes the empty table view elemetns
      * @method addEmptyRow
      * @todo: (xabel) refactor this to include a "addRow" method in _models[0]
      */
      addEmptyRow: function() {
        this.dataModel.addRow({ at: 0});
        this.cleanEmptyTableInfo();
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowChanged
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowChanged: function() {
        this.model.notice('Saving your edit.');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSynched: function() {
        this.model.notice('Sucessfully saved');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowFailed: function() {
        this.model.notice('Oops, there has been an error saving your changes.');
      },

      /**
      * Captures the destroy event from the row and produces a notification
      * @method rowDestroyed
      */

      rowDestroyed: function() {
        this.model.notice('Sucessfully deleted')
        this._checkEmptyTable();
      }

    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',
      tabClass: cdb.admin.TableView,

      initialize: function() {
        this.tableView = new this.tabClass({
          dataModel: this.model.data(),
          model: this.model,
          sqlView: this.options.sqlView,
          geocoder: this.options.geocoder
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

    // cdb.admin.PublicTableTab = cdb.admin.TableTab

})();
<commit_msg>table scroll performance improved<commit_after>
(function() {

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({
      classLabel: 'cdb.admin.TableView',
      events: cdb.core.View.extendEvents({
          'click .clearview': '_clearView',
          'click .sqlview .export_query': '_tableFromQuery',
          'click .noRows': 'addEmptyRow'
      }),

      rowView: cdb.admin.RowView,

      initialize: function() {
        var self = this;
        this.elder('initialize');
        this.options.row_header = true;
        this.model.data().bind('newPage', this.newPage, this);
        _.bindAll(this, "render", "addEmptyRow", "_checkEmptyTable", "_forceScroll", "_scrollMagic", "rowChanged", "rowSynched", "rowFailed", "rowDestroyed", "emptyTable");
        var topReached = false;
        this._editorsOpened = null;

         // pagination
        setInterval(function() {
          if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
            return;
          }
          var pos = $(this).scrollTop();
          var d = self.model.data();
          // do not let to fetch previous pages
          // until the user dont scroll back a little bit
          if(pos > 2) {
            topReached = false;
          }
          var pageSize = $(window).height() - self.$el.offset().top;
          var tableHeight = this.$('tbody').height();
          var realPos = pos + pageSize;
          if(tableHeight < pageSize) {
            return;
          }
          if(realPos > tableHeight) {
             d.setPage(d.getPage() + 1);
          } else if (pos <= 0) {
            if(!topReached) {
              d.setPage(d.getPage() - 1);
            }
            topReached = true;
          }
        }, 2000);

        // Initialize moving header and loaders when scrolls
        this.scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop(), last: 'vertical' };
        $(window).scroll( this._scrollMagic );

        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$el[fn]("

Loading more rows

Now viewing 120 of 264

");
        });

        this.model.data().bind('endLoadingRows', function() {
          //self.$el.find('.page_loader').remove();
        });

        this.bind('cellDblClick', this._editCell, this);
        this.model.bind('change:dataSource', this._onSQLView, this);
        // when model changes the header is re rendered so the notice should be added
        this.model.bind('change', this._onSQLView, this);
        this.model.bind('dataLoaded', function() {
          self._checkEmptyTable();
          self._forceScroll();
        });

        this.bind('createRow', function() {
          self._checkEmptyTable();
        });

        // this.model.bind('loadModelStarted', function() {
        //   self.$el.fadeOut()
        // })

        // this.model.bind('loadModelCompleted', function() {
        //   self.$el.fadeIn()
        // })

        // this.model.bind('loadModelFailed', function() {
        //   self.$el.fadeIn()
        // })

      },

      render: function(args) {
        this.elder('render', args);
        this.trigger('render');
      },

      _onSQLView: function() {
        // if we are in a SQL view add a header
        this.$('.sqlview').remove();
        if(this.model.isInSQLView()) {
          var empty = this.isEmptyTable();

          this.$('thead').append(
            this.getTemplate('table/views/sql_view_notice')({ empty: empty })
          );

          this.$('thead > tr').css('height', 64 + 42);
          if(this.isEmptyTable()) {
            this.addEmptySQLIfo();
          }
        }
      },

      _clearView: function(e) {
        e.preventDefault();
        this.trigger('clearSQLView');
        //this.model.useSQLView(null);
        return false;
      },

      _tableFromQuery: function(e) {
        var self = this;
        e.preventDefault();
        //this.model.useSQLView(null);
        var dlg = new cdb.admin.TableNameDialog({
          ok: function(name) {
            var sql = self.model.data().getSQL();
            alert('to be done in backend');
            /*
            var table = cdb.admin.CartoDBTableMetadata.createFromQuery(name, sql);

            table.save(null, {
              success: function() {
              },
              error: function() {
                cdb.log.error("problem creating table from query");
              }
            });
            */
          }
        });
        dlg.appendToBody().show();
        return false;
        /*
        from_query:SELECT * FROM cb_municipios_5000_e limit 1
        name:asdasdasd
        */
      },

      /**
       *  Function to control the scroll in the table (horizontal and vertical)
       */
      _scrollMagic: function(ev) {
        var actual_scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop() };

        if (this.scroll_position.x != actual_scroll_position.x) {
          this.scroll_position.x = actual_scroll_position.x;
          this.$el.find("thead").addClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "vertical") {
            this.scroll_position.x = actual_scroll_position.x;
            this.$el.find("thead > tr > th > div").css({"marginLeft": "0px"})
            this.$el.find("thead div > div").css("top", actual_scroll_position.y + "px");
            this.scroll_position.last = "horizontal";
          }

        } else if (this.scroll_position.y != actual_scroll_position.y) {
          this.scroll_position.y = actual_scroll_position.y;
          this.$el.find("thead").removeClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "horizontal") {
            this.$el.find("thead > tr > th > div")
              .css({"marginLeft": "-" + actual_scroll_position.x + "px"})
            this.$el.find("thead > tr > th > div > div").removeAttr('style')
            this.scroll_position.last = "vertical";
          }
        }
      },

      _getEditor: function(columnType, opts) {
        var editors = {
          'string': cdb.admin.EditStringDialog,
          'number': cdb.admin.EditNumberDialog,
          'date': cdb.admin.EditDateDialog,
          'geometry': cdb.admin.EditGeometryDialog,
          'boolean': cdb.admin.EditBooleanDialog
        };

        // clean previous if exits
        var view = this._editorsOpened;
        if(view) {
          view.clean();
        }

        var Editor = editors[columnType];
        this._editorsOpened = new Editor(opts);
        return this._editorsOpened;

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        if(this.model.data().isReadOnly()) {
          return;
        }
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var row = self.model.data().getRowAt(y);

        var initial_value = '';
        if(self.model.data().getCell(y, column) === 0 || self.model.data().getCell(y, column) === '0') {
          initial_value = '0';
        } else if (self.model.data().getCell(y, column)) {
          initial_value = self.model.data().getCell(y, column);
        }
        var dlg = this._getEditor(columnType, {
          initial_value: initial_value,
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        if(!dlg) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }

        // auto add to table view
        // Check first if the row is the first or the cell is the last :)
        var $td = $(e.target).closest("td")
          , offset = $td.position()
          , width = $td.outerWidth();

        if ($td.parent().index() == 0) {
          offset.top += 5;
        } else {
          offset.top -= 11;
        }

        if ($td.index() == ($td.size() - 1)) {
          offset.left -= 22;
        } else {
          offset.left -= 11;
        }

        dlg.showAt(offset.left, offset.top, width, true);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        var self = this;
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({
            column: column,
            table: this.model,
            sqlView: this.options.sqlView
          }).bind('georeference', function(column) {
              var dlg = new cdb.admin.GeoreferenceDialog({
                model: this.table,
                wizard_option: 1, /** geocode using a column */
                georeference_column: column,
                geocoder: self.options.geocoder
              });
              dlg.appendToBody().open();
          });

          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      },

      /**
      * Checks if the table has any rows, and if not, launch the method for showing the appropiate view elements
      * @method _checkEmptyTable
      */
      _checkEmptyTable: function() {
        if(this.isEmptyTable()) {
          this.addEmptyTableInfo();
        } else {
          this.cleanEmptyTableInfo();
        }
      },

      /**
      * Force the table to be at the beginning
      * @method _forceScroll
      */
      _forceScroll: function(ev){
        $(window).scrollLeft(0);
      },

      /**
      * Adds the view elements associated with no content in the table
      * @method addemptyTableInfo
      */
      addEmptyTableInfo: function() {
        if(this.$('.noRows').length == 0 && !this.model.isInSQLView()) {
          this.elder('addEmptyTableInfo');

          this.$el.hide();
          var columnsNumber = this.model.get('schema').length;
          var columns = '
+	';
          for(var i = 0; i < columnsNumber; i++) {
            columns += '	';
          }
          columns += '
';
          var columnsFooter = '
	';
          for(var i = 0; i < columnsNumber; i++) {
            columnsFooter += '	';
          }
          columnsFooter += '
';

          var $columns = $(columns+columnsFooter)
          this.$el.append($columns);

          this.template_base = cdb.templates.getTemplate('table/views/empty_table');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Adds the view elements associated with no content in the table when a SQL is applied
      * @method addEmptySQLIfo
      */
      addEmptySQLIfo: function() {
        if(this.model.isInSQLView()) {
          this.elder('addEmptySQLInfo');
          this.$('tbody').html('');
          this.$('tfoot').remove();
          this.$el.hide();

          this.template_base = cdb.templates.getTemplate('table/views/empty_sql');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Removes from the view the no-content elements
      * @method cleanEmptyTableInfo
      */
      cleanEmptyTableInfo: function() {
        this.$('tfoot').fadeOut('fast', function() {
          $(this).remove();
        })
        this.$('.noRows').slideUp('fast', function() {
            $(this).remove();
        })
      },

      /**
      * Add a new row and removes the empty table view elemetns
      * @method addEmptyRow
      * @todo: (xabel) refactor this to include a "addRow" method in _models[0]
      */
      addEmptyRow: function() {
        this.dataModel.addRow({ at: 0});
        this.cleanEmptyTableInfo();
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowChanged
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowChanged: function() {
        this.model.notice('Saving your edit.');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSynched: function() {
        this.model.notice('Sucessfully saved');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowFailed: function() {
        this.model.notice('Oops, there has been an error saving your changes.');
      },

      /**
      * Captures the destroy event from the row and produces a notification
      * @method rowDestroyed
      */

      rowDestroyed: function() {
        this.model.notice('Sucessfully deleted')
        this._checkEmptyTable();
      }

    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',
      tabClass: cdb.admin.TableView,

      initialize: function() {
        this.tableView = new this.tabClass({
          dataModel: this.model.data(),
          model: this.model,
          sqlView: this.options.sqlView,
          geocoder: this.options.geocoder
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

    // cdb.admin.PublicTableTab = cdb.admin.TableTab

})();
<|endoftext|>


<commit_before>
var graphs = [];      // rickshaw objects
var datum = [];       // metric data
var aliases = [];     // alias strings
var realMetrics = []; // non-false targets

// minutes of data in the live feed
var period = (typeof period == 'undefined') ? 5 : period;

// gather our non-false targets
function gatherRealMetrics() {
  var falseTargets = 0;
  for (var i=0; i<metrics.length; i++) {
    if (metrics[i].target === false) {
    falseTargets++;
    } else {
      realMetrics[i - falseTargets] = metrics[i];
    }
  }
}

// build our graph objects
function constructGraphs() {
  for (var i=0; i<realMetrics.length; i++) {
    aliases[i] = realMetrics[i].alias || realMetrics[i].target;
    datum[i] = [{ x:0, y:0 }];
    graphs[i] = new Rickshaw.Graph({
      element: document.querySelector('.graph' + i),
      width: 348,
      height: 100,
      interpolation: 'step-after',
      series: [{
        name: aliases[i],
        color: '#afdab1',
        data: datum[i]
      }]
    });
    graphs[i].render();
  }
}

// construct url
var myUrl;
function constructUrl(period) {
  var targets = "";
  for (var i=0; i<realMetrics.length; i++) {
    if (i != 0) {
      targets += '&';
    }
    targets += ('target=' + encodeURI(realMetrics[i].target));
  }
  myUrl = url + '/render/?' + targets + '&from=-' + period + 'minutes&format=json';
}

// refresh the graph
function refreshData(immediately) {

  getData(function(values) {
    for (var i=0; i<graphs.length; i++) {
      for (var j=0; j<values[i].length; j++) {
        if (typeof values[i][j] !== "undefined") {
          datum[i][j] = values[i][j];
        }
      }

      // check our thresholds and update color
      var lastValue = datum[i][datum[i].length - 1].y;
      var warning = realMetrics[i].warning;
      var critical = realMetrics[i].critical;
      if (critical > warning) {
        if (lastValue >= critical) {
          graphs[i].series[0].color = '#d59295';
        } else if (lastValue >= warning) {
          graphs[i].series[0].color = '#f5cb56';
        } else {
          graphs[i].series[0].color = '#afdab1';
        }
      } else {
        if (lastValue <= critical) {
          graphs[i].series[0].color = '#d59295';
        } else if (lastValue <= warning) {
          graphs[i].series[0].color = '#f5cb56';
        } else {
          graphs[i].series[0].color = '#afdab1';
        }
      }
      // we want to render immediately, i.e.
      // as soon as ajax completes
      // used for time period / pause view
      if (immediately) {
        updateGraphs(i);
      }
    }
    values = null;
  });

  // we can wait until all data is gathered, i.e.
  // the live refresh should happen synchronously
  if (!immediately) {
    for (var i=0; i<graphs.length; i++) {
      updateGraphs(i);
    }
  }
}

// retrieve the data from Graphite
function getData(cb) {
  var myDatum = [];
  $.ajax({
    beforeSend: function(xhr) {
      if (auth.length > 0) {
        var bytes = Crypto.charenc.Binary.stringToBytes(auth);
        var base64 = Crypto.util.bytesToBase64(bytes);
        xhr.setRequestHeader("Authorization", "Basic " + base64);
      }
    },
    dataType: 'json',
    error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
    url: myUrl
  }).done(function(d) {
    if (d.length > 0) {
      for (var i=0; i<d.length; i++) {
        myDatum[i] = [];
        myDatum[i][0] = {
          x: d[i].datapoints[0][1],
          y: d[i].datapoints[0][0] || graphs[i].lastKnownValue || 0
        };
        for (var j=1; j<d[i].datapoints.length; j++) {
          myDatum[i][j] = {
            x: d[i].datapoints[j][1],
            y: d[i].datapoints[j][0] || graphs[i].lastKnownValue
          };
          if (typeof d[i].datapoints[j][0] === "number") {
            graphs[i].lastKnownValue = d[i].datapoints[j][0];
          }
        }
      }
    }
    cb(myDatum);
  });
}

// perform the actual graph object and
// overlay name and number updates
function updateGraphs(i) {
  // update our graph
  graphs[i].update();
  if (datum[i][datum[i].length - 1] !== undefined) {
    var lastValue = datum[i][datum[i].length - 1].y;
    var lastValueDisplay;
    if ((typeof lastValue == 'number') && lastValue < 2.0) {
      lastValueDisplay = Math.round(lastValue*1000)/1000;
    } else {
      lastValueDisplay = parseInt(lastValue);
    }
    $('.overlay-name' + i).text(aliases[i]);
    $('.overlay-number' + i).text(lastValueDisplay);
    if (realMetrics[i].unit) {
      $('.overlay-number' + i).append('' + realMetrics[i].unit + '');
    }
  } else {
    $('.overlay-name' + i).text(aliases[i]);
    $('.overlay-number' + i).html('NF');
  }
}

// add our containers
function buildContainers() {
  var falseTargets = 0;
  for (var i=0; i<metrics.length; i++) {
    if (metrics[i].target === false) {
      $('#main').append('

');
      falseTargets++;
    } else {
      var j = i - falseTargets;
      $('#main').append(
        '
' +
        '

' +
        '

' +
        '
'
      );
    }
  }
}

// filter out false targets
gatherRealMetrics();

// build our div containers
buildContainers();

// build our graph objects
constructGraphs();

// set our last known value at invocation
Rickshaw.Graph.prototype.lastKnownValue = 0;

// build our url
constructUrl(period);

// set our theme
var myTheme = (typeof theme == 'undefined') ? 'default' : theme;
if (myTheme === "dark") { enableNightMode(); }

// hide our toolbar if necessary
var toolbar = (typeof toolbar == 'undefined') ? true : toolbar;
if (!toolbar) { $('div#toolbar').css('display', 'none'); }

// initial load screen
for (var i=0; i<graphs.length; i++) {
  if (realMetrics[i].target === false) {
    //continue;
  } else if (myTheme === "dark") {
    $('.overlay-number' + i).html('');
  } else {
    $('.overlay-number' + i).html('');
  }
}
refreshData("now");

// define our refresh and start interval
var refreshInterval = (typeof refresh == 'undefined') ? 2000 : refresh;
var refreshId = setInterval(refreshData, refreshInterval);

// set our "live" interval hint
$('#toolbar ul li.timepanel a.play').text(period + 'min');

// activate night mode
function enableNightMode() {
  $('body').addClass('night');
  $('div#title h1').addClass('night');
  $('div#graph svg').css('opacity', '0.8');
  $('div#overlay-name').addClass('night');
  $('div#overlay-number').addClass('night');
  $('div#toolbar ul li.timepanel').addClass('night');
}

// deactivate night mode
function disableNightMode() {
  $('body').removeClass('night');
  $('div#title h1').removeClass('night');
  $('div#graph svg').css('opacity', '1.0');
  $('div#overlay-name').removeClass('night');
  $('div#overlay-number').removeClass('night');
  $('div#toolbar ul li.timepanel').removeClass('night');
}

// activate night mode by click
$('li.toggle-night').on('click', 'a', function() {
  if ($('body').hasClass('night')) {
    disableNightMode();
  } else {
    enableNightMode();
  }
});

// toggle number display
$('li.toggle-nonum').on('click', 'a', function() { $('div#overlay-number').toggleClass('nonum'); });

// time panel, pause live feed and show range
$('#toolbar ul li.timepanel').on('click', 'a.range', function() {
  var period = $(this).attr("title");
  for (var i=0; i<realMetrics.length; i++) {
    constructUrl(period);
  }
  if (! $('#toolbar ul li.timepanel a.play').hasClass('pause')) {
    $('#toolbar ul li.timepanel a.play').addClass('pause');
  }
  $('#toolbar ul li.timepanel a.play').text('paused');
  $(this).parent('li').parent('ul').find('li').removeClass('selected');
  $(this).parent('li').addClass('selected');
  refreshData("now");
  clearInterval(refreshId);
});

// time panel, resume live feed
$('#toolbar ul li.timepanel').on('click', 'a.play', function() {
  for (var i=0; i<realMetrics.length; i++) {
    constructUrl(5);
  }
  $(this).parent('li').parent('ul').find('li').removeClass('selected');
  $(this).parent('li').addClass('selected');
  $(this).removeClass('pause');
  $('#toolbar ul li.timepanel a.play').text(period + 'min');
  refreshData("now");
  // explicitly clear the old Interval in case
  // someone "doubles up" on the live play button
  clearInterval(refreshId);
  // remove and recreate the original graphs[]
  // helps clear out any rendering artifacts
  $('#graph svg').remove();
  constructGraphs();
  // reapply our style settings if night mode is active
  if ($('body').hasClass('night')) { enableNightMode(); }
  // restart our refresh interval
  refreshId = setInterval(refreshData, refreshInterval);
});

<commit_msg>use Graphite's native keepLastValue() as our null padding, refs #14 make padnulls a dashboard option, refs #35<commit_after>
var graphs = [];      // rickshaw objects
var datum = [];       // metric data
var aliases = [];     // alias strings
var realMetrics = []; // non-false targets

// minutes of data in the live feed
var period = (typeof period == 'undefined') ? 5 : period;

// gather our non-false targets
function gatherRealMetrics() {
  var falseTargets = 0;
  for (var i=0; i<metrics.length; i++) {
    if (metrics[i].target === false) {
    falseTargets++;
    } else {
      realMetrics[i - falseTargets] = metrics[i];
    }
  }
}

// build our graph objects
function constructGraphs() {
  for (var i=0; i<realMetrics.length; i++) {
    aliases[i] = realMetrics[i].alias || realMetrics[i].target;
    datum[i] = [{ x:0, y:0 }];
    graphs[i] = new Rickshaw.Graph({
      element: document.querySelector('.graph' + i),
      width: 348,
      height: 100,
      interpolation: 'step-after',
      series: [{
        name: aliases[i],
        color: '#afdab1',
        data: datum[i]
      }]
    });
    graphs[i].render();
  }
}

// construct url
var myUrl;
var padnulls = (typeof padnulls == 'undefined') ? true : padnulls;
function constructUrl(period) {
  var targets = "";
  for (var i=0; i<realMetrics.length; i++) {
    if (i != 0) {
      targets += '&';
    }
    if (padnulls === true) {
      targets += ('target=keepLastValue(' + encodeURI(realMetrics[i].target) + ')');
    } else {
      targets += ('target=' + encodeURI(realMetrics[i].target));
    }
  }
  myUrl = url + '/render/?' + targets + '&from=-' + period + 'minutes&format=json';
}

// refresh the graph
function refreshData(immediately) {

  getData(function(values) {
    for (var i=0; i<graphs.length; i++) {
      for (var j=0; j<values[i].length; j++) {
        if (typeof values[i][j] !== "undefined") {
          datum[i][j] = values[i][j];
        }
      }

      // check our thresholds and update color
      var lastValue = datum[i][datum[i].length - 1].y;
      var warning = realMetrics[i].warning;
      var critical = realMetrics[i].critical;
      if (critical > warning) {
        if (lastValue >= critical) {
          graphs[i].series[0].color = '#d59295';
        } else if (lastValue >= warning) {
          graphs[i].series[0].color = '#f5cb56';
        } else {
          graphs[i].series[0].color = '#afdab1';
        }
      } else {
        if (lastValue <= critical) {
          graphs[i].series[0].color = '#d59295';
        } else if (lastValue <= warning) {
          graphs[i].series[0].color = '#f5cb56';
        } else {
          graphs[i].series[0].color = '#afdab1';
        }
      }
      // we want to render immediately, i.e.
      // as soon as ajax completes
      // used for time period / pause view
      if (immediately) {
        updateGraphs(i);
      }
    }
    values = null;
  });

  // we can wait until all data is gathered, i.e.
  // the live refresh should happen synchronously
  if (!immediately) {
    for (var i=0; i<graphs.length; i++) {
      updateGraphs(i);
    }
  }
}

// retrieve the data from Graphite
function getData(cb) {
  var myDatum = [];
  $.ajax({
    beforeSend: function(xhr) {
      if (auth.length > 0) {
        var bytes = Crypto.charenc.Binary.stringToBytes(auth);
        var base64 = Crypto.util.bytesToBase64(bytes);
        xhr.setRequestHeader("Authorization", "Basic " + base64);
      }
    },
    dataType: 'json',
    error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
    url: myUrl
  }).done(function(d) {
    if (d.length > 0) {
      for (var i=0; i<d.length; i++) {
        myDatum[i] = [];
        myDatum[i][0] = { x: d[i].datapoints[0][1], y: d[i].datapoints[0][0] };
        for (var j=0; j<d[i].datapoints.length; j++) {
          myDatum[i][j] = { x: d[i].datapoints[j][1], y: d[i].datapoints[j][0] };
        }
      }
    }
    cb(myDatum);
  });
}

// perform the actual graph object and
// overlay name and number updates
function updateGraphs(i) {
  // update our graph
  graphs[i].update();
  if (datum[i][datum[i].length - 1] !== undefined) {
    var lastValue = datum[i][datum[i].length - 1].y;
    var lastValueDisplay;
    if ((typeof lastValue == 'number') && lastValue < 2.0) {
      lastValueDisplay = Math.round(lastValue*1000)/1000;
    } else {
      lastValueDisplay = parseInt(lastValue);
    }
    $('.overlay-name' + i).text(aliases[i]);
    $('.overlay-number' + i).text(lastValueDisplay);
    if (realMetrics[i].unit) {
      $('.overlay-number' + i).append('' + realMetrics[i].unit + '');
    }
  } else {
    $('.overlay-name' + i).text(aliases[i]);
    $('.overlay-number' + i).html('NF');
  }
}

// add our containers
function buildContainers() {
  var falseTargets = 0;
  for (var i=0; i<metrics.length; i++) {
    if (metrics[i].target === false) {
      $('#main').append('

');
      falseTargets++;
    } else {
      var j = i - falseTargets;
      $('#main').append(
        '
' +
        '

' +
        '

' +
        '
'
      );
    }
  }
}

// filter out false targets
gatherRealMetrics();

// build our div containers
buildContainers();

// build our graph objects
constructGraphs();

// build our url
constructUrl(period);

// set our theme
var myTheme = (typeof theme == 'undefined') ? 'default' : theme;
if (myTheme === "dark") { enableNightMode(); }

// hide our toolbar if necessary
var toolbar = (typeof toolbar == 'undefined') ? true : toolbar;
if (!toolbar) { $('div#toolbar').css('display', 'none'); }

// initial load screen
for (var i=0; i<graphs.length; i++) {
  if (realMetrics[i].target === false) {
    //continue;
  } else if (myTheme === "dark") {
    $('.overlay-number' + i).html('');
  } else {
    $('.overlay-number' + i).html('');
  }
}
refreshData("now");

// define our refresh and start interval
var refreshInterval = (typeof refresh == 'undefined') ? 2000 : refresh;
var refreshId = setInterval(refreshData, refreshInterval);

// set our "live" interval hint
$('#toolbar ul li.timepanel a.play').text(period + 'min');

// activate night mode
function enableNightMode() {
  $('body').addClass('night');
  $('div#title h1').addClass('night');
  $('div#graph svg').css('opacity', '0.8');
  $('div#overlay-name').addClass('night');
  $('div#overlay-number').addClass('night');
  $('div#toolbar ul li.timepanel').addClass('night');
}

// deactivate night mode
function disableNightMode() {
  $('body').removeClass('night');
  $('div#title h1').removeClass('night');
  $('div#graph svg').css('opacity', '1.0');
  $('div#overlay-name').removeClass('night');
  $('div#overlay-number').removeClass('night');
  $('div#toolbar ul li.timepanel').removeClass('night');
}

// activate night mode by click
$('li.toggle-night').on('click', 'a', function() {
  if ($('body').hasClass('night')) {
    disableNightMode();
  } else {
    enableNightMode();
  }
});

// toggle number display
$('li.toggle-nonum').on('click', 'a', function() { $('div#overlay-number').toggleClass('nonum'); });

// time panel, pause live feed and show range
$('#toolbar ul li.timepanel').on('click', 'a.range', function() {
  var period = $(this).attr("title");
  for (var i=0; i<realMetrics.length; i++) {
    constructUrl(period);
  }
  if (! $('#toolbar ul li.timepanel a.play').hasClass('pause')) {
    $('#toolbar ul li.timepanel a.play').addClass('pause');
  }
  $('#toolbar ul li.timepanel a.play').text('paused');
  $(this).parent('li').parent('ul').find('li').removeClass('selected');
  $(this).parent('li').addClass('selected');
  refreshData("now");
  clearInterval(refreshId);
});

// time panel, resume live feed
$('#toolbar ul li.timepanel').on('click', 'a.play', function() {
  for (var i=0; i<realMetrics.length; i++) {
    constructUrl(5);
  }
  $(this).parent('li').parent('ul').find('li').removeClass('selected');
  $(this).parent('li').addClass('selected');
  $(this).removeClass('pause');
  $('#toolbar ul li.timepanel a.play').text(period + 'min');
  refreshData("now");
  // explicitly clear the old Interval in case
  // someone "doubles up" on the live play button
  clearInterval(refreshId);
  // remove and recreate the original graphs[]
  // helps clear out any rendering artifacts
  $('#graph svg').remove();
  constructGraphs();
  // reapply our style settings if night mode is active
  if ($('body').hasClass('night')) { enableNightMode(); }
  // restart our refresh interval
  refreshId = setInterval(refreshData, refreshInterval);
});

<|endoftext|>


<commit_before>(function() {
	var defaultExercise = {
	 fromId: null,
	 toId: null,
	 blink: false,
	 blinkSpeed: 0,
	 reps: 0,
	 duration: 0,
	 name: ''
	};

	var exerciseId = null;

	function getPercentage(value, total) {
		return value / total * 100;
	}

	function getElementPositionPercentaje(id) {
		var pos = {};
		var $element = $('#'+id);
		var position = $element.position();
		var height = $element.parent().height();
		var width = $element.parent().width();
		pos.top = getPercentage(position.top, height);
		pos.left = getPercentage(position.left, width);
		return pos;
	}

	function getData() {
		var exercise = defaultExercise;
		exercise.fromId = $('#from-input').val();
		exercise.toId = $('#to-input').val();
		exercise.blink = $('#blink-check').prop('checked');
		exercise.blinkSpeed = $('#blink-speed-input').val();
		exercise.duration = $('#duration-input').val();
		exercise.reps = $('#reps-input').val();
		exercise.name = $('#name-input').val();
		exercise.rest = $('#rest-input').val();
		return exercise;
	}

	function setData(exercise) {
		console.log(exercise);
		$('#from-input').val(exercise.fromId);
		$('#to-input').val(exercise.toId);
		$('#blink-check').prop('checked', exercise.blink);
		if(!$('#blink-check').prop('checked')) {
			$('#blink-speed-input').attr('disabled', true);
		} else {
			$('#blink-speed-input').removeAttr('disabled');
		}
		$('#blink-speed-input').val(exercise.blinkSpeed );
		$('#duration-input').val(exercise.duration);
		$('#reps-input').val(exercise.reps);
		$('#name-input').val(exercise.name);
		$('#rest-input').val(exercise.rest);
	}

	function loadForm() {
		exerciseId = common.getParameterByName('id');
		if(exerciseId) {
			$('#submit-btn').parent().hide();
			$('#update-btn').parent().show();
			exerciseService.getExercise(exerciseId).then(setData, onError);
		}
	}

	function onError(data) {
		common.onError(data);
		restorePage();
	}

	function restorePage() {
		/*$('#exercise-form').trigger("reset");
		$('#submit-btn').parent().show();
		$('#update-btn').parent().hide();
		exerciseId = null;*/
		//window.history.pushState({}, document.title, "/admin.html");
		window.location.href = '/exercise-list.html';
	}

	function onSuccessUpdate(msg, ) {
		alert('Exercise Updated');
	    console.log("saved" , msg );
	    restorePage();
	}

	function onSuccessSave(msg) {
		alert('Exercise Saved');
	    console.log("saved" , msg );
	    restorePage();
	}

	function isValidForm(data) {
		return data.fromId !== data.toId && data.name !== undefined && data.name !== '';
	}

	$(document).ready(function ready(){
		$('#update-btn').parent().hide();
		var formData = null;
		var submitExercise = null;
		loadForm();

		$('#blink-check').on('change', function(e) {
			if(!$(e.target).prop('checked')) {
				$('#blink-speed-input').attr('disabled', true);
			} else {
				$('#blink-speed-input').removeAttr('disabled');
			}
		});

		$('#submit-btn').on('click', function(e) {
			e.preventDefault();
			//$('#exercise-form').validator();
			formData = getData();
			submitExercise = formData;
			submitExercise.from = getElementPositionPercentaje('span' + formData.fromId);
			submitExercise.to = getElementPositionPercentaje('span' + formData.toId);

			if(isValidForm(formData) && !$(e.target).hasClass('disabled')) {
				exerciseService.saveExercise(submitExercise).then(onSuccessSave, common.onError);
			}else {
				alert('Some fields are missing. From and To should be different');
			}

		});

		$('#update-btn').on('click', function(e) {
			e.preventDefault();

			formData = getData();
			submitExercise = formData;
			submitExercise.from = getElementPositionPercentaje('span' + formData.fromId);
			submitExercise.to = getElementPositionPercentaje('span' + formData.toId);
			console.log(submitExercise);
			if(isValidForm(formData) && !$(e.target).hasClass('disabled')) {
				exerciseService.updateExercise(exerciseId, submitExercise).then(onSuccessUpdate, common.onError);
			}else {
				alert('Some fields are missing. From and To should be different');
			}
		});
	});

})();

<commit_msg>Update admin.js<commit_after>(function() {
	var defaultExercise = {
	 fromId: null,
	 toId: null,
	 blink: false,
	 blinkSpeed: 0,
	 reps: 0,
	 duration: 0,
	 name: ''
	};

	var exerciseId = null;

	function getPercentage(value, total) {
		return value / total * 100;
	}

	function getElementPositionPercentaje(id) {
		var pos = {};
		var $element = $('#'+id);
		var position = $element.position();
		var height = $element.parent().height();
		var width = $element.parent().width();
		pos.top = getPercentage(position.top, height);
		pos.left = getPercentage(position.left, width);
		return pos;
	}

	function getData() {
		var exercise = defaultExercise;
		exercise.fromId = $('#from-input').val();
		exercise.toId = $('#to-input').val();
		exercise.blink = $('#blink-check').prop('checked');
		exercise.blinkSpeed = $('#blink-speed-input').val();
		exercise.duration = $('#duration-input').val();
		exercise.reps = $('#reps-input').val();
		exercise.name = $('#name-input').val();
		exercise.rest = $('#rest-input').val();
		return exercise;
	}

	function setData(exercise) {
		console.log(exercise);
		$('#from-input').val(exercise.fromId);
		$('#to-input').val(exercise.toId);
		$('#blink-check').prop('checked', exercise.blink);
		if(!$('#blink-check').prop('checked')) {
			$('#blink-speed-input').attr('disabled', true);
		} else {
			$('#blink-speed-input').removeAttr('disabled');
		}
		$('#blink-speed-input').val(exercise.blinkSpeed );
		$('#duration-input').val(exercise.duration);
		$('#reps-input').val(exercise.reps);
		$('#name-input').val(exercise.name);
		$('#rest-input').val(exercise.rest);
	}

	function loadForm() {
		exerciseId = common.getParameterByName('id');
		if(exerciseId) {
			$('#submit-btn').parent().hide();
			$('#update-btn').parent().show();
			exerciseService.getExercise(exerciseId).then(setData, onError);
		}
	}

	function onError(data) {
		common.onError(data);
		restorePage();
	}

	function restorePage() {
		/*$('#exercise-form').trigger("reset");
		$('#submit-btn').parent().show();
		$('#update-btn').parent().hide();
		exerciseId = null;*/
		//window.history.pushState({}, document.title, "/admin.html");
		window.location.href = '/exercise-list.html';
	}

	function onSuccessUpdate(msg) {
		alert('Exercise Updated');
	    console.log("saved" , msg );
	    restorePage();
	}

	function onSuccessSave(msg) {
		alert('Exercise Saved');
	    console.log("saved" , msg );
	    restorePage();
	}

	function isValidForm(data) {
		return data.fromId !== data.toId && data.name !== undefined && data.name !== '';
	}

	$(document).ready(function ready(){
		$('#update-btn').parent().hide();
		var formData = null;
		var submitExercise = null;
		loadForm();

		$('#blink-check').on('change', function(e) {
			if(!$(e.target).prop('checked')) {
				$('#blink-speed-input').attr('disabled', true);
			} else {
				$('#blink-speed-input').removeAttr('disabled');
			}
		});

		$('#submit-btn').on('click', function(e) {
			e.preventDefault();
			//$('#exercise-form').validator();
			formData = getData();
			submitExercise = formData;
			submitExercise.from = getElementPositionPercentaje('span' + formData.fromId);
			submitExercise.to = getElementPositionPercentaje('span' + formData.toId);

			if(isValidForm(formData) && !$(e.target).hasClass('disabled')) {
				exerciseService.saveExercise(submitExercise).then(onSuccessSave, common.onError);
			}else {
				alert('Some fields are missing. From and To should be different');
			}

		});

		$('#update-btn').on('click', function(e) {
			e.preventDefault();

			formData = getData();
			submitExercise = formData;
			submitExercise.from = getElementPositionPercentaje('span' + formData.fromId);
			submitExercise.to = getElementPositionPercentaje('span' + formData.toId);
			console.log(submitExercise);
			if(isValidForm(formData) && !$(e.target).hasClass('disabled')) {
				exerciseService.updateExercise(exerciseId, submitExercise).then(onSuccessUpdate, common.onError);
			}else {
				alert('Some fields are missing. From and To should be different');
			}
		});
	});

})();

<|endoftext|>