text: var global_status = {};
  global_status.main_vis = "cells"; // Selected from 4 options on top
  /* Options are:
      'cells'
      'genes'
      'tree' (this is being phased out)
  */
  
  // Determine the projected coordinates
  global_status.plotted_projectionX = [null, null];  // [Name of projection dataframe, Name of column]
  global_status.plotted_projectionY = [null, null];
  global_status.plotted_trajectory = "";
  
  // Indicate whether or not we have pooled data
  global_status.pooled = false;
  
  // Is trajectory data available?
  global_status.has_tree = false;
  global_status.has_sigs = true;
  global_status.has_mods = true;
  global_status.has_lca = false;
  
  // Dendro specific values
  global_status.dendro_collapse_settings = {};
  
  // Determine projected values
  global_status.plotted_item = "";  // name of signature, meta or gene that is plotted
  global_status.plotted_item_type = ""; // either 'signature', 'protein', 'meta', 'gene', or 'signature-gene'
  
  global_status.cluster_var = ""; // which cluster variable are we using
  
  global_status.selected_cell = ""; // which cell(s) is/are currently selected
  global_status.selection_type = "none"; // either 'cell', or 'cells', or 'pool', or 'pools', or 'none'
  global_status.selection_name = "Selection"; // For plot legends
  
  // handling for scatterplot of modulescors vs signature scores
  global_status.enrichment = false;
  global_status.enrichment_module= "";
  
  
  var global_data = {};
  
  global_data.sig_projection_coordinatesX = {};
  global_data.sig_projection_coordinatesY = {};
  global_data.tree_projection_coordinates = {};
  global_data.plotted_values = {}; // Holds gene expression, signature scores/ranks, etc...
  global_data.sig_info = {};  // Holds the information for the last plotted signature
  global_data.meta_sigs = []; // Holds list of sig names that are meta-data
  global_data.extra_plotted_values = {}; // Holds extra info like module hotspot scores
  global_data.mod_gene_list = {}; // Holds extra info like module hotspot scores
  
  var lower_left_content;
  var upper_left_content;
  var right_content;
  
  function get_global_data(key){
      return global_data[key];
  }
  
  function get_global_status(key){
      return global_status[key];
  }
  
  function set_global_status(update){
  
      // Delete all updates that are not changes
      var toDelete = []
      _.forIn(update, function(value, key) {
          if (global_status[key] === value && !key.endsWith('_changed'))
          {
              toDelete.push(key);
          }
      });
      _.forEach(toDelete, function(key) {
          delete update[key]
      });
  
      // Now all updates represent changes
  
      _.forIn(update, function(value, key) {
          global_status[key] = value;
      });
  
      // Now all global_status are current, and the 'update' dict
      // holds keys for entries that just changed
      //
      var all_promises = [];
      var right_content_promises = [];
      var lower_left_content_promises = [];
      var upper_left_content_promises = [];
  
      right_content.setLoadingStatus(true);
      lower_left_content.setLoadingStatus(true);
      upper_left_content.setLoadingStatus(true);
  
      // Updates plotted item values
      if('plotted_item' in update || 'plotted_item_type' in update) {
  
          var type = get_global_status('plotted_item_type');
          var sig_key = get_global_status('plotted_item');
  
          var val_promise;
  
          if(type === 'signature'){
              val_promise = api.signature.scores(sig_key)
          } else if (type === 'protein') {
              val_promise = api.protein.values(sig_key)
          } else if (type === 'meta') {
              val_promise = api.signature.meta(sig_key)
          } else if (type === 'gene' || type === 'signature-gene') {
              val_promise = api.expression.gene(sig_key)
          } else {
              throw 'Bad "plotted_item_type"!';
          }
  
          var val_promise_after = val_promise.then(function(values)
          {
              global_data.plotted_values = values;
          });
  
          all_promises.push(val_promise_after);
          right_content_promises.push(val_promise_after);
          lower_left_content_promises.push(val_promise_after);
          upper_left_content_promises.push(val_promise_after);
      }
  
      // Updates signature info if we're plotting a new signature
      if('plotted_item' in update || 'plotted_item_type' in update) {
  
          var type = get_global_status('plotted_item_type');
          var new_sig = get_global_status('plotted_item');
          var sig_info = get_global_data('sig_info');
  
          if((type === 'signature') &&
              (_.isEmpty(sig_info) || sig_info.name !== new_sig)
          )
          {
              var sig_info_promise = api.signature.info(new_sig)
                  .then(new_info => {
                      global_data.sig_info = new_info;
                  })
  
              all_promises.push(sig_info_promise);
              lower_left_content_promises.push(sig_info_promise);
  
          }
  
      }
      
      if('enrichment' in update || 'enrichment_module' in update) {
          var enriched = get_global_status("enrichment")
          if(enriched) {
              var mod_sig_promise = api.modules.hotspot_score(get_global_status("enrichment_module"))
              .then(new_info => {
                  global_data.extra_plotted_values = new_info;
              })
          
              all_promises.push(mod_sig_promise);
              lower_left_content_promises.push(mod_sig_promise);
              
              
              var mod_gene_list_promise = api.modules.mod_gene_list(get_global_status("enrichment_module"))
              .then(new_info => {
                  global_data.mod_gene_list = new_info;
              })
          
              all_promises.push(mod_gene_list_promise);
              lower_left_content_promises.push(mod_gene_list_promise);
              
              
          }
          
      } else if ('plotted_item' in update || 'plotted_item_type' in update) {
          global_status["enrichment"] = false;
      }
      
      $.when.apply($, right_content_promises).then(
          function() {
              right_content.update(update).then(() => {
                  right_content.setLoadingStatus(false);
              })
          }
      );
  
      $.when.apply($, lower_left_content_promises).then(
          function() {
              lower_left_content.update(update).then(() => {
                  lower_left_content.setLoadingStatus(false);
              })
          }
      );
  
      $.when.apply($, upper_left_content_promises).then(
          function() {
              upper_left_content.update(update).then(() => {
                  upper_left_content.setLoadingStatus(false);
              })
          }
      );
  
  
  }
  
  window.onresize = function()
  {
      right_content.resize()
      lower_left_content.resize()
  
      /*
      if($('#heatmap-div').is(":visible"))
      {
          $('#heatmap-div').find('svg').remove();
          global_heatmap = new HeatMap('#heatmap-div');
      }
  
      if ($('#tree_div').is(":visible"))
      {
          $('#tree_div').find('svg').remove();
      }
      */
  
      //Render
      //drawHeat();
  
  };
  
  window.onload = function()
  {
  
      lower_left_content = new Lower_Left_Content()
      upper_left_content = new Upper_Left_Content()
      right_content = new Right_Content()
  
  
      var right_promise = right_content.init();
  
      var sessionInfoPromise = api.sessionInfo().then(info => {
          if(info.name.length > 0){
              $('#SampleNameSpan').text(' - ' + info.name)
              document.title = 'VISION - ' + info.name
          }
  
          if(info.has_tree){
              $('#nav-bar')
                  .find(".nav-link[data-main-vis='tree']")
                  .removeClass('disabled')
          }
  
          if(info.has_mods){
              $('#nav-bar')
                  .find(".nav-link[data-main-vis='genes']")
                  .removeClass('disabled')
          }
          
          
          if(info.has_seurat){
              // Enable all seurat options
              $('#de_test_type > option').prop("disabled", false)
          }
  
          global_data.meta_sigs = info.meta_sigs
          global_status.pooled = info.pooled
          global_status.ncells = info.ncells
          global_status.has_tree = info.has_tree
          global_status.has_lca = info.has_lca
          global_status.has_sigs = info.has_sigs
          global_status.has_mods = info.has_mods
          global_status.has_proteins = info.has_proteins
          global_status.has_dendrogram = info.has_dendrogram
          global_status.dendrogram = info.dendrogram
  
          global_data.clusters_unique = info.clusters
      })
  
      var allCellClustersPromise = api.clusters.meta_levels()
          .then(function(data) {
              global_data.meta_levels = data;
          })
  
  
      var combinedPromise = $.when(sessionInfoPromise, allCellClustersPromise)
          .then(function(){
              return upper_left_content.init();
          })
  
      var combinedPromise2 = $.when(sessionInfoPromise, allCellClustersPromise)
          .then(function(){
              return lower_left_content.init();
          })
  
      // When it's all done, run this
      $.when(right_promise, combinedPromise, combinedPromise2, allCellClustersPromise)
          .then(function(){
              var has_tree = get_global_status('has_tree')
              var update0 = {'main_vis': (has_tree ? 'tree': 'cells')}
              var update1 = upper_left_content.select_default_sig();
              var update2 = right_content.select_default_proj();
              var update = Object.assign({}, update0, update1, update2); // Merge
              set_global_status(update)
  
              if (has_tree){ // need to change styling for navbar button to select Traj
                  $('#nav-bar').find('.nav-link').removeClass('active')
                  $('a[data-main-vis="tree"]').addClass('active')
              }
          });
  
      // Enable the nav-bar functionality
      $('#nav-bar').find('.nav-link').click(function(){
          if( $(this).hasClass('disabled') ) { return; }
          $('#nav-bar').find('.nav-link').removeClass('active')
          $(this).addClass('active')
          set_global_status({
              'main_vis': $(this).data('main-vis')
          })
      });
  
      window.addEventListener("select-cells", function(e) {
  
          var cells = e.detail.cells;
          var update = {}
  
          if (cells.length == 0){
              update['selection_type'] = 'none'
              update['selected_cell'] = cells
              set_global_status(update)
              return;
          }
  
          if(get_global_data('pooled')){
              if(cells.length == 1){
                  update['selected_cell'] = cells
                  update['selection_type'] = 'pool'
              } else {
                  update['selected_cell'] = cells
                  update['selection_type'] = 'pools'
              }
          } else {
              if(cells.length == 1){
                  update['selected_cell'] = cells
                  update['selection_type'] = 'cell'
              } else {
                  update['selected_cell'] = cells
                  update['selection_type'] = 'cells'
              }
          }
  
          var name = 'Selection'
          if('name' in e.detail){
              name = e.detail.name
          }
          update['selection_name'] = name;
  
          set_global_status(update)
      });
  
      // Pre-cache loading spinner
      var img = $('<img />', {
          src: 'css/loading.svg',
          alt: 'loading-spinner'
      });
  
  };
  