incubator-blur-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From amccu...@apache.org
Subject [25/50] [abbrv] Initial Blur Console commit.
Date Fri, 17 May 2013 03:24:52 GMT
http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
new file mode 100644
index 0000000..fd5918e
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
@@ -0,0 +1,197 @@
+//= require jquery.dataTables
+//= require datatables.fnReloadAjax
+//= require_self
+
+$.extend($.fn.dataTableExt.oStdClasses, {
+  "sSortAsc": "header headerSortDown",
+  "sSortDesc": "header headerSortUp",
+  "sSortable": "header"
+});
+
+$(document).ready(function() {
+  var visible_column_count = $('#queries-table thead th').length;
+  var refresh_rate = -1;
+  var refresh_timeout = null;
+  var data_table = null;
+  var load_queries = function() {
+    data_table = $('#queries-table').dataTable({
+      "sDom": "<'row'<'span4'i><'span2'r><'span3'f>>t",
+      bPaginate: false,
+      bProcessing: true,
+      bAutoWidth: false,
+      bDeferRender: true,
+      "oLanguage": {
+        "sInfoEmpty": "",
+        "sInfo": "Displaying _TOTAL_ queries",
+        "sSearch": "Filter queries:",
+        "sZeroRecords": "No queries to display",
+        "sInfoFiltered": "(filtered from _MAX_ total queries)"
+      },
+
+      sAjaxSource: Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, 1, {format: 'json'}),
+      aoColumns: table_cols(),
+      fnRowCallback: process_row
+    });
+    data_table.fnSort([[4, 'desc']]);
+    add_refresh_rates(data_table);
+    $('#queries-table').ajaxComplete(function(e, xhr, settings) {
+      if (settings.url.indexOf('/blur_queries/refresh') >= 0) {
+        if (refresh_rate > -1) {
+          refresh_timeout = setTimeout(function() {
+            var range_time_limit = $('.time_range').find('option:selected').val();
+            data_table.fnReloadAjax(Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, range_time_limit, {format: 'json'}));
+          }, refresh_rate * 1000);
+        }
+      }
+    });
+    $('.time_range').live('change', function() {
+      var range_time_limit = $(this).find('option:selected').val();
+      data_table.fnReloadAjax(Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, range_time_limit, {format: 'json'}));
+    });
+    $('.filter_option').on('click', function(){
+      var container = $(this);
+      var index = visible_column_count - 2;
+      var filter_string = container.attr("data-filter");
+      $('#queries-table').dataTable().fnFilter(filter_string, index);
+    })
+  };
+
+  var table_cols = function() {
+    if (visible_column_count === 8) {
+      return [
+        {
+          "mDataProp": "userid",
+					"sWidth": "85px"
+        }, {
+          "mDataProp": "query",
+          "sWidth": "500px"
+        }, {
+          "mDataProp": "tablename",
+					"sWidth": "75px"
+        }, {
+          "mDataProp": "start",
+					"sWidth": "85px"
+        }, {
+          "mDataProp": "time",
+					"sWidth": "95px"
+        }, {
+          "mDataProp": "status",
+          "sWidth": "100px"
+        }, {
+          "mDataProp": "state",
+          "bVisible": false
+        }, {
+          "mDataProp": "action",
+					"sWidth": "100px"
+        }
+      ];
+    }
+    return [
+      {
+        "mDataProp": "userid"
+      }, {
+        "mDataProp": "tablename"
+      }, {
+        "mDataProp": "start"
+      }, {
+        "mDataProp": "time"
+      }, {
+        "mDataProp": "status",
+        "sWidth": "150px"
+      }, {
+        "mDataProp": "state",
+        "bVisible": false
+      }, {
+        "mDataProp": "action"
+      }
+    ];
+  };
+  var process_row = function(row, data, rowIdx, dataIdx) {
+    var action_td = $('td:last-child', row);
+    if (action_td.html() === '') {
+      action_td.append("<a href='" + (Routes.blur_query_path(data['id'])) + "' class='more_info' style='margin-right: 3px'>More Info</a>");
+      if (data['state'] === 'Running' && data['can_update']) {
+        action_td.append("<form accept-charset='UTF-8' action='" + (Routes.cancel_blur_query_path(data['id'])) + "' class='cancel' data-remote='true' method='put'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put'></div><input class='cancel_query_button btn btn-small' type='submit' value='Cancel'><img src='/assets/loading.gif' style='display:none'></form>");
+      }
+    }
+    var time = data.time.substring(0, data.time.indexOf(' ')).split(':');
+    var timeModifier = data.time.substring(data.time.indexOf(' ') + 1) === 'PM';
+    var timeInSecs = (timeModifier ? parseInt(time[0], 10) + 12 : parseInt(time[0], 10)) * 3600 + parseInt(time[1], 10) * 60 + parseInt(time[2], 10);
+    var dateNow = new Date();
+    var timeNowSecs = dateNow.getHours() * 3600 + dateNow.getMinutes() * 60 + dateNow.getSeconds();
+    if (data.state === 'Running' && Math.abs(timeNowSecs - timeInSecs) > 60) {
+      $(row).addClass('oldRunning');
+    }
+    return row;
+  };
+  var add_refresh_rates = function(data_table) {
+    var refresh_content = '<div class="span3">Auto Refresh: <div class="btn-group">';
+    var options = [
+      {
+        'key': 'Off',
+        'value': -1
+      }, {
+        'key': '10s',
+        'value': 10
+      }, {
+        'key': '1m',
+        'value': 60
+      }, {
+        'key': '10m',
+        'value': 600
+      }
+    ];
+    $.each(options, function(idx, val) {
+      var link_class = idx === 0 ? 'btn-primary' : '';
+      refresh_content += "<a href='javascript:void(0)' class='refresh_option " + link_class + " btn' data-refresh_val='" + val.value + "'>" + val.key + "</a>";
+    });
+    refresh_content += '</div></div>';
+    $('#queries-table_wrapper > .row:first-child').prepend(refresh_content);
+    $('.dataTables_wrapper .row .span3:first-child .btn-group').append('<a id="refresh-queries" class="btn"><i class="icon-refresh"/></a>');
+    $('#refresh-queries').click(function() {
+        clearTimeout(refresh_timeout);
+        data_table.fnReloadAjax();
+    });
+    $('a.refresh_option').click(function() {
+      $('a.refresh_option').removeClass('btn-primary');
+      $(this).addClass('btn-primary');
+      var prev_refresh_rate = refresh_rate;
+      refresh_rate = $(this).data('refresh_val');
+      if (prev_refresh_rate === -1) {
+        data_table.fnReloadAjax();
+      }
+      else if (refresh_rate === -1 && refresh_timeout)
+      {
+        clearTimeout(refresh_timeout);
+      }
+    });
+  };
+  var truncate = function(value, length, ommission) {
+    if (!value) return null;
+    if (!(value.length > length)) return value;
+    return "" + (value.substring(0, length)) + (ommission != null ? ommission : {
+      ommission: ''
+    });
+  };
+  $('.more_info').live('click', function(e) {
+    var self = $(this);
+    self.after(Spinner.clone());
+    e.preventDefault();
+    $.ajax({
+      url: self.attr('href'),
+      type: 'GET',
+      success: function(data) {
+        self.siblings('#loading-spinner').remove();
+        $().popup({
+          title: "Additional Info",
+          titleClass: 'title',
+          body: data
+        });
+      }
+    });
+  });
+  $('.cancel_query_button').live('click', function() {
+    $(this).siblings('img').show();
+  });
+  load_queries();
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
new file mode 100644
index 0000000..22db2f4
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
@@ -0,0 +1,13 @@
+//= require jquery.dynatree
+//= require bootstrap-tooltip
+//= require bootstrap-popover
+//= require sorttable
+//= require_tree .
+
+$(document).ready(function() {
+  // Dynatree Setup
+  $.ui.dynatree.nodedatadefaults["icon"] = false;
+
+  // Create the cluster collection and start the stream
+  new ClusterCollection().stream({interval: 10000, update: true});
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
new file mode 100644
index 0000000..9d67205
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
@@ -0,0 +1,279 @@
+var Cluster = Backbone.Model.extend({
+  defaults: {},
+  initialize: function(){
+    this.view = new ClusterView({model: this, id: 'cluster_' + this.id});
+    this.build_child_tables();
+    this.set_running_query_header_state();
+    this.on('change:blur_tables', function(){
+      this.update_child_tables();
+    });
+    this.on('change:safe_mode', function(){
+      $('li#cluster_tab_' + this.get('id') + ' .safemode-icon').toggle();
+    });
+    this.on('table_has_been_queried', function(){
+      this.set_running_query_header_state();
+    });
+  },
+  build_child_tables: function(){
+    this.set({tables: new TableCollection(this.get('blur_tables'), {cluster: this})}, {silent: true});
+  },
+  update_child_tables: function(){
+    this.get('tables').update(this.get('blur_tables'));
+    this.view.set_table_values();
+  },
+  set_running_query_header_state: function(){
+    var tables_queried = this.get('tables').where({queried_recently: true}).length;
+    if (tables_queried > 0) {
+      $('li#cluster_tab_' + this.get('id') + ' .queries-running-icon').show();
+    } else {
+      $('li#cluster_tab_' + this.get('id') + ' .queries-running-icon').hide();
+    }
+  },
+  enable_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'disabled', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      $().popup({
+        title: "Enable Tables",
+        titleClass: 'title',
+        body: "Are you sure you want to enable these tables?",
+        btns: {
+          "Enable": {
+            "class": "primary",
+            func: _.bind(function() {
+              $.ajax({
+                type: 'PUT',
+                url: Routes.enable_zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+                data: {tables: table_ids}
+              });
+              $().closePopup();
+              _.each(selected_tables, function(table){
+                table.set({table_status: 5});
+              });
+              this.view.set_table_state();
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  disable_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'active', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      $().popup({
+        title: "Disable Tables",
+        titleClass: 'title',
+        body: "Are you sure you want to disable these tables?",
+        btns: {
+          "Disable": {
+            "class": "primary",
+            func: _.bind(function() {
+              $.ajax({
+                type: 'PUT',
+                url: Routes.disable_zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+                data: {tables: table_ids}
+              });
+              $().closePopup();
+              _.each(selected_tables, function(table){
+                table.set({table_status: 3});
+              });
+              this.view.set_table_state();
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  delete_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'disabled', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      var delete_tables_send = function(delete_index) {
+        $.ajax({
+          type: 'DELETE',
+          url: Routes.zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+          data: {
+            tables: table_ids,
+            delete_index: delete_index
+          }
+        });
+        $().closePopup();
+        _.each(selected_tables, function(table){
+          table.set({table_status: 1});
+        });
+        this.view.set_table_state();
+      };
+      $().popup({
+        title: "Delete Tables",
+        titleClass: 'title',
+        body: "Do you want to delete all of the underlying table indicies?",
+        btns: {
+          "Delete tables and indicies": {
+            "class": "danger",
+            func: _.bind(delete_tables_send, this, true)
+          },
+          "Delete tables only": {
+            "class": "warning",
+            func: _.bind(delete_tables_send, this, false)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  send_action_request: function(selected_tables, confirm_function){
+    if (_.find(selected_tables, function(table){
+        return table.get('queried_recently') && table.get('state') == 'active';
+    })){
+      $().popup({
+        title: 'Warning! You are attempting to change an active table!',
+        titleClass: 'title',
+        body: 'You are attempting to perform an action on a recently queried table, Do you wish to continue?',
+        btns: {
+          "Continue": { class: 'danger', func: confirm_function },
+          "Cancel": { func: function() { $().closePopup(); } }
+        }
+      });
+    } else {
+      confirm_function();
+    }
+  },
+  perform_action: function(state, action){
+    var selected_tables = this.get('tables').where({state: state, checked: true});
+    if (_.find(selected_tables, function(table){ return table.get('queried_recently'); })){
+      $().popup({
+        title: 'Warning! You are attempting to ' + action + ' an active table!',
+        titleClass: 'title',
+        body: '<div>You are attempting to perform an action on a recently queried table, Do you wish to continue?</div>',
+        btns: {
+          "Enable": {
+            class: 'danger',
+            func: _.bind(function(){
+              this.move_forward_with_action(action, selected_tables);
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    } else {
+      this.move_forward_with_action(action, selected_tables);
+    }
+  }
+});
+
+var ClusterCollection = Backbone.StreamCollection.extend({
+  model: Cluster,
+  url: Routes.zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+  initialize: function(models, options){
+    this.on('add', function(collection){
+      var container = $('#tables-wrapper');
+      var renderedView = $(collection.view.render().el);
+      container.children('div').length === 0 ? container.html(renderedView.addClass('active')) : container.append(renderedView);
+    });
+  }
+});
+
+var ClusterView = Backbone.View.extend({
+  className: 'tab-pane cluster',
+  template: JST['templates/blur_table/cluster_view'],
+  events: {
+    'click .bulk-action-checkbox' : 'set_table_state',
+    'click .check-all' : 'check_all_boxes',
+    'click .btn[data-action=enable]' : 'enable_tables',
+    'click .btn[data-action=disable]' : 'disable_tables',
+    'click .btn[data-action=delete]' : 'delete_tables'
+  },
+  colspan_lookup : {'active': 6, 'disabled': 4},
+  render: function(){
+    this.$el.html(this.template({cluster: this.model}));
+    this.populate_tables();
+    return this;
+  },
+  populate_tables: function(){
+    var el = $(this.el);
+    this.model.get('tables').each(function(table){
+      elementToAdd = table.view.el.rendered ? table.view.el : table.view.render().el;
+      var table_parent = el.find('.' + table.get('table') + '-table');
+      table_parent.append(elementToAdd);
+      table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+      sorttable.makeSortable(table_parent.parent()[0]);
+    });
+    this.set_table_values();
+  },
+  set_table_values: function(){
+    var table_prefixes = ['active', 'disabled'];
+    for (var index = 0; index < table_prefixes.length; index++){
+      var table = this.$el.find('.' + table_prefixes[index] + '-table');
+      table.find('.no-data').remove();
+      var table_children_count = table.children().length;
+      this.$el.find('.' + table_prefixes[index] + '-counter').text(table_children_count);
+      if (this.model.get('tables').where({table: table_prefixes[index]}).length <= 0){
+        table.append(this.no_table(this.colspan_lookup[table_prefixes[index]]));
+      }
+    }
+  },
+  set_table_state: function(){
+    var checked_count = this.$el.find('tbody tr.highlighted-row').length;
+    var set_checkbox_state = _.bind(function(){
+      var row_count = this.$el.find('tbody:visible tr:not(.no-data, .changing-state)').length;
+      var check_all = this.$el.find('.tab-pane.active .check-all');
+      if (checked_count === row_count){
+        if (checked_count === 0){
+          check_all.removeAttr('checked');
+          check_all.attr('disabled', 'disabled');
+        } else {
+          check_all.attr('checked', 'checked');
+        }
+      } else {
+        check_all.removeAttr('checked');
+      }
+    }, this);
+    var set_button_state = _.bind(function(){
+      var toggle_button = this.$el.find('.tab-pane.active button');
+      checked_count > 0 ? toggle_button.removeAttr('disabled') : toggle_button.attr('disabled', 'disabled');
+    }, this);
+    set_checkbox_state();
+    set_button_state();
+  },
+  check_all_boxes: function(){
+    var check_all = this.$el.find('.tab-pane.active .check-all');
+    if (check_all.is(':checked')){
+      this.$el.find('.tab-pane.active .bulk-action-checkbox:not(:checked)').click();
+    } else {
+      this.$el.find('.tab-pane.active .bulk-action-checkbox:checked').click();
+    }
+  },
+  no_table: function(colspan){
+    return $('<tr class="no-data"><td/><td colspan="' + colspan + '">No Tables for this Section</td></tr>')
+  },
+  enable_tables: function(event){
+    this.model.enable_tables();
+    this.model.change();
+  },
+  disable_tables: function(event){
+    this.model.disable_tables();
+    this.model.change();
+  },
+  delete_tables: function(event){
+    this.model.delete_tables();
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
new file mode 100644
index 0000000..3762a55
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
@@ -0,0 +1,189 @@
+//= require flot/flot
+
+var Table = Backbone.Model.extend({
+  defaults: {
+    'checked' : false,
+  },
+  state_lookup : ['deleted', 'deleting', 'disabled', 'disabling', 'active', 'enabling'],
+  table_lookup : ['deleted', 'disabled', 'disabled', 'active', 'active', 'disabled'],
+  colspan_lookup : {'active': 6, 'disabled': 5},
+  initialize: function(){
+    this.view = new TableView({model: this});
+    this.view.render();
+    this.set({
+      state: this.state_lookup[this.get('table_status')],
+      table: this.table_lookup[this.get('table_status')]
+    });
+    this.on('change:table_status', function(){
+      var table = this.get('table')
+      this.set({
+        state: this.state_lookup[this.get('table_status')],
+        table: this.table_lookup[this.get('table_status')],
+        checked: false
+      }, {
+        silent: true
+      });
+      if (this.get('table') !== table){
+        var table_parent = this.collection.cluster.view.$el.find('.' + this.get('table') + '-table');
+        table_parent.append(this.view.el);
+        table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+        sorttable.makeSortable(table_parent.parent()[0]);
+      }
+    });
+    this.on('change:queried_recently', function(){
+      this.collection.cluster.trigger('table_has_been_queried');
+    });
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  parse_uri: function(piece){
+    var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
+    var result = parse_url.exec(this.get('table_uri'));
+    var index = _.indexOf(['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'], piece);
+    if (index === -1) throw 'The index, ' + piece + ' does not exist as a part of a uri.';
+    return result[index];
+  },
+  get_terms: function(request_data, success){
+    $.ajax({
+      type: 'GET',
+      url: Routes.terms_blur_table_path(this.get('id'), {format: 'json'}),
+      data: request_data,
+      success: success,
+      error:function (xhr, ajaxOptions, thrownError){
+        alert(thrownError + ": Terms Currently Unavailable");
+      }
+    });
+  },
+  capitalize_first: function(word){
+    return word.charAt(0).toUpperCase() + word.slice(1);
+  }
+});
+
+var TableCollection = Backbone.StreamCollection.extend({
+  model: Table,
+  initialize: function(models, options){
+    this.cluster = options.cluster;
+    this.on('add', function(table){
+      var table_parent = table.collection.cluster.view.$el.find('.' + this.get('table') + '-table');
+      table_parent.append(table.view.el);
+      table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+      sorttable.makeSortable(table_parent.parent()[0]);
+    });
+    this.on('remove', function(table){
+      table.view.destroy();
+    });
+  }
+});
+
+var TableView = Backbone.View.extend({
+  tagName: 'tr',
+  className: 'blur_table',
+  events: {
+    'click .bulk-action-checkbox' : 'toggle_row',
+    'click .hosts' : 'show_hosts',
+    'click .info' : 'show_schema',
+    'click .comments' : 'show_comments'
+  },
+  template: JST['templates/blur_table/table_row'],
+  render: function(){
+    this.rendered = true;
+    this.$el.removeClass('changing-state')
+    this.$el.attr('blur_table_id', this.model.get('id')).html(this.template({table: this.model})).removeClass('highlighted-row');
+    if (this.model.get('checked')) this.$el.addClass('highlighted-row').find('.bulk-action-checkbox').prop('checked', 'checked');
+    if (['disabling', 'enabling', 'deleting'].indexOf(this.model.get('state')) >= 0) this.$el.addClass('changing-state');
+    return this;
+  },
+  toggle_row: function(){
+    this.model.set({checked: !this.model.get('checked')}, {silent: true});
+    this.$el.toggleClass('highlighted-row');
+  },
+  show_hosts: function(){
+    $.ajax({
+      type: 'GET',
+      url: Routes.hosts_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+      success: _.bind(function(data){
+        var host_modal = $(JST['templates/blur_table/hosts']({table: this.model, hosts: data}));
+        this.setup_filter_tree(host_modal);
+        $().popup({
+          title: 'Additional Host/Shard Info',
+          titleClass: 'title',
+          body: host_modal
+        });
+      }, this)
+    });
+    return false;
+  },
+  show_schema: function(){
+    $.ajax({
+      type: 'GET',
+      url: Routes.schema_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+      success: _.bind(function(data){
+        var schema_modal = $(JST['templates/blur_table/schema']({table: this.model, schema: data}));
+        this.setup_filter_tree(schema_modal.find('.table_info_tree'));
+        $().popup({
+          title: 'Additional Schema Info',
+          titleClass: 'title',
+          body: schema_modal,
+        });
+        var table_model = this.model;
+        schema_modal.on('click', '.terms', function(){
+          var clicked_element = $(this);
+          var request_data =
+          {
+            family: $(this).attr('data-family-name'),
+            column: $(this).attr('data-column-name'),
+            startwith: ' ',
+            size: 20
+          };
+          table_model.get_terms(request_data, _.bind(function(data) {
+            new TermsView({
+              clicked_element: clicked_element,
+              parent: this,
+              terms: data,
+              family: request_data.family,
+              column: request_data.column,
+              table_id: this.get('id')})
+            .render();
+          }, table_model));
+        });
+      }, this)
+    });
+    return false;
+  },
+  show_comments: function(){
+    var comment_modal = $(JST['templates/blur_table/comments']({table: this.model}));
+    $().popup({
+      title: 'Comments',
+      titleClass: 'title',
+      body: comment_modal ,
+      btns: {
+        "Submit": {
+          "class": "primary",
+          func: _.bind(function() {
+            var input_comment = document.getElementById("comments").value;
+            $.ajax({
+              type: 'PUT',
+              url: Routes.comment_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+              data: {comment: input_comment},
+              success: _.bind(function(){
+                this.model.set({comments: input_comment});
+              }, this)
+            });
+
+            $().closePopup();
+          }, this)
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+          }
+        }
+      }
+    });
+    return false;
+  },
+  setup_filter_tree: function(selector) {
+    return selector.dynatree();
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/terms.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/terms.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/terms.js
new file mode 100644
index 0000000..2cf8b30
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/terms.js
@@ -0,0 +1,99 @@
+var TermsView = Backbone.View.extend({
+  className: 'terms-view',
+  template: JST['templates/blur_table/terms_view'],
+  render: function(){
+    this.popover = this.options.clicked_element.popover({
+      title: this.options.column + " terms<i class='icon-remove popover-close' style='position:absolute; top:15px;right:15px'></i>",
+      content: this.template(this.options),
+      trigger: 'focus',
+      placement: 'right'
+    }).popover('show');
+    this.set_buttons_state();
+
+    // Declare all events separate of the events hash
+    // because the popover clones the html and reference
+    // is lost
+    $('.popover')
+      .on('click', function(event){event.stopPropagation();})
+      .on('click', '.popover-close', _.bind(this.close_popover, this))
+      .on('click', '.more-terms-btn:not(.disabled)', _.bind(this.get_more_terms, this))
+      .on('click', '.reset-terms', _.bind(this.refresh_list, this))
+      .on('click', '.search-term-link', _.bind(this.redirect_to_search, this))
+      .on('click', '.term-search-btn', _.bind(this.search_for_terms, this))
+      .on('keydown', _.bind(this.search_using_enter, this));
+    $('html').on('click', _.bind(this.close_popover, this));
+  },
+  close_popover: function(){
+    this.popover.popover('hide');
+    $('.popover').off('click');
+    $('html').off('click');
+    this.destroy();
+  },
+  get_terms: function(request_data, success){
+    this.options.parent.get_terms(request_data, success);
+  },
+  // Get another set of terms starting at the last list position
+  // append it to the current list
+  get_more_terms: function(){
+    var spinner = Spinner.clone();
+    $('.more-terms').append(spinner);
+    var last_term = $('.term-li:last-child > span:last-child > span').text();
+    this.get_terms({
+      family: this.options.family,
+      column: this.options.column,
+      startwith: last_term,
+      size: 21
+    }, _.bind(function(data) {
+      data.shift(1);
+      spinner.remove();
+      $('.popover .terms-list').append(JST['templates/blur_table/terms_list']({terms: data}));
+    }, this));
+  },
+  // Get the first set of terms resetting the search
+  // overwrites the list
+  refresh_list: function(){
+    var spinner = Spinner.clone();
+    $('.more-terms').append(spinner);
+    this.get_terms({
+      family: this.options.family,
+      column: this.options.column,
+      startwith: ' ',
+      size: 20
+    }, _.bind(function(data) {
+      spinner.remove();
+      $('.popover .terms-list').html(JST['templates/blur_table/terms_list']({terms: data}));
+    }, this));
+  },
+  search_for_terms: function(){
+    var spinner = Spinner.clone();
+    $('.more-terms').append(spinner);
+    var last_term = $('.popover .term-search').val();
+    this.options.parent.get_terms({
+      family: this.options.family,
+      column: this.options.column,
+      startwith: last_term,
+      size: 20
+    }, _.bind(function(data) {
+      spinner.remove();
+      $('.popover .terms-list').html(JST['templates/blur_table/terms_list']({terms: data}));
+      this.set_buttons_state();
+    }, this));
+  },
+  search_using_enter: function(event){
+    if (event.which === 13) {
+      event.preventDefault();
+      this.search_for_terms();
+    }
+  },
+  redirect_to_search: function(event){
+    var term = $(event.currentTarget).siblings('.input').children('span').text();
+    window.location = Routes.zookeeper_searches_path(CurrentZookeeper)
+      + ("?table_id=" + this.options.table_id + "&query=")
+      + encodeURIComponent(this.options.family + "." + this.options.column + ":" + term);
+  },
+  set_buttons_state: function(){
+    if ($('.popover .terms-list').children().length < 20){
+      $('.popover .more-terms-btn').addClass('disabled');
+    }
+  }
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/dashboard.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/dashboard.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/dashboard.js
new file mode 100644
index 0000000..9d6db3e
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/dashboard.js
@@ -0,0 +1,7 @@
+//= require_tree
+
+$(document).ready(function() {
+  // Create the hdfs and blur instances and start streaming
+  new HdfsCollection().stream({interval: 5000, update: true});
+  new ZookeeperCollection().stream({interval: 5000, update: true});
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/hdfs.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/hdfs.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/hdfs.js
new file mode 100644
index 0000000..80f0747
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/hdfs.js
@@ -0,0 +1,55 @@
+//=require flot/flot
+//=require flot/jquery.flot.pie.min.js
+
+var Hdfs = Backbone.Model.extend({
+  initialize: function(){
+    this.view = new HdfsView({model: this});
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  node_width: function(){
+    var stats = this.get('most_recent_stats');
+    return Math.round((stats.live_nodes / stats.total_nodes) * 100);
+  },
+  usage_width: function(){
+    var stats = this.get('most_recent_stats');
+    return Math.round((stats.dfs_used_real / stats.config_capacity) * 100);
+  },
+  percent_used: function(){
+    var usage_percent = this.usage_width();
+    if (usage_percent < 1) {
+      return '< 1%';
+    }
+    return usage_percent + '%';
+  }
+});
+
+var HdfsCollection = Backbone.StreamCollection.extend({
+  model: Hdfs,
+  url: Routes.hdfs_index_path({format: 'json'}),
+  initialize: function(models, options){
+    this.on('add', function(hdfs){
+      $('#hdfses').append(hdfs.view.render().el);
+    });
+    this.on('remove', function(hdfs){
+      hdfs.view.destroy();
+    });
+  }
+});
+
+var HdfsView = Backbone.View.extend({
+  className: 'hdfs_info online',
+  events: {
+    'click' : 'navigate_to_hdfs'
+  },
+  template: JST['templates/dashboard/hdfs'],
+  render: function(){
+    this.$el.html(this.template({hdfs: this.model}));
+    return this;
+  },
+  navigate_to_hdfs: function(){
+    var id = this.model.get('id');
+    window.location = Routes.hdfs_index_path() + '/' + id + '/show';
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/long_running.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/long_running.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/long_running.js
new file mode 100644
index 0000000..02b1db2
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/long_running.js
@@ -0,0 +1,23 @@
+var LongRunningView = Backbone.View.extend({
+  tagName: 'ul',
+  className: 'modal-list',
+  events: {
+    'click .icon-remove' : 'cancel_query'
+  },
+  template: JST['templates/dashboard/long_running'],
+  render: function(data){
+    this.$el.html(this.template({data: data}));
+    return this;
+  },
+  cancel_query: function(event){
+    var self = $(event.target);
+    var id = $(event.target.parentElement).attr('data-id');
+    $.ajax({
+      type: 'PUT',
+      url: Routes.cancel_blur_query_path(id, {format: 'json'}),
+      success: function(){
+        self.closest('li').remove();
+      }
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/zookeeper.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/zookeeper.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/zookeeper.js
new file mode 100644
index 0000000..0febc1d
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/dashboard/zookeeper.js
@@ -0,0 +1,136 @@
+//= require flot/flot
+//= require flot/jquery.flot.pie.min.js
+
+var Zookeeper = Backbone.Model.extend({
+  initialize: function(){
+    this.view = new ZookeeperView({model: this});
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  consistent_controller_versions: function(){
+    return this.get('controller_version') === 1;
+  },
+  consistent_shard_versions: function(){
+    return this.get('shard_version') === 1;
+  },
+  online_controller_nodes: function(){
+    return this.get('controller_total') - this.get('controller_offline_node');
+  },
+  online_shard_nodes: function(){
+    return this.get('shard_total') - this.get('shard_offline_node');
+  },
+  controller_progress_width: function(){
+    return Math.round((this.online_controller_nodes() / this.get('controller_total')) * 100)
+  },
+  shard_progress_width: function(){
+    return Math.round((this.online_shard_nodes() / this.get('shard_total')) * 100)
+  },
+  status_image: function(){
+    var state, img;
+    switch(this.get('zookeeper_status'))
+    {
+      case 0:
+        state = "offline.";
+        img = 'offline';
+        break;
+      case 1:
+        state = "online.";
+        img = "online";
+        break;
+      case 2:
+        state = "in a quorum warning state.";
+        img = "warning";
+        break;
+      case 3:
+        state = "experiencing a quorum failure.";
+        img = 'failure';
+        break;
+      default:
+        state = "offline.";
+        img = 'offline';
+        break;
+    }
+    return '<img src="/assets/' + img + '.png" title="Zookeeper is ' + state + '"/>'
+  }
+});
+
+var ZookeeperCollection = Backbone.StreamCollection.extend({
+  model: Zookeeper,
+  url: Routes.zookeepers_path({format: 'json'}),
+  initialize: function(models, options){
+    this.on('add', function(zookeeper){
+      $('#zookeepers').append(zookeeper.view.render().el);
+    });
+    this.on('remove', function(zookeeper){
+      zookeeper.view.destroy();
+    });
+  }
+});
+
+var ZookeeperView = Backbone.View.extend({
+  className: 'zookeeper_info',
+  events: {
+    'click' : 'navigate_to_zookeeper',
+    'click .warning' : 'show_long_running'
+  },
+  template: JST['templates/dashboard/zookeeper'],
+  render: function(){
+    this.$el.html(this.template({zookeeper: this.model}));
+    if (this.$el.find('.cont-chart')[0]){
+      this.draw_zk_charts(this.$el.find('.cont-chart')[0], this.model.get('controller_total'), this.model.get('controller_offline_node'));
+    }
+    if (this.$el.find('.shard-chart')[0]){
+      this.draw_zk_charts(this.$el.find('.shard-chart')[0], this.model.get('shard_total'), this.model.get('shard_offline_node'));
+    }
+    return this;
+  },
+  navigate_to_zookeeper: function(){
+    window.location = Routes.zookeeper_path(this.model.get('id'));
+  },
+  show_long_running: function(){
+    $.ajax({
+      type: 'GET',
+      url: Routes.long_running_queries_zookeeper_path(this.model.get('id'), {format: 'json'}),
+      success: function(data){
+        $().popup({
+          title: "Long Running Queries",
+          titleClass: 'title',
+          body: new LongRunningView().render(data).el
+        });
+      }
+    })
+    return false;
+  },
+  draw_zk_charts: function(target, total, offline){
+    var options = {
+      series: {
+        pie: {
+          show: true,
+          radius: 1,
+          innerRadius: 0.63,
+          label: {
+            show: false
+          }
+        }
+      },
+      legend: {
+        show: false
+      }
+    };
+    if (total == 0) {
+      var data = [
+        { label: "None", data: 1, color: "#CED7DA" }
+      ];
+    }
+    else {
+      var data = [
+        { label: "Online", data: total - offline, color: "#7DC77D" },
+        { label: "Offline", data: offline, color: "#FF1919" }
+      ];
+    }
+    target.style.width = '135px';
+    target.style.height = '135px';
+    $.plot(target, data, options);
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/cluster.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/cluster.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/cluster.js
new file mode 100644
index 0000000..10d256b
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/cluster.js
@@ -0,0 +1,69 @@
+var ClusterModel = Backbone.Model.extend({
+  initialize: function(){
+    this.view = new ClusterView({model: this});
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  url: function(){
+    return '/clusters/' + this.get('id') + '.json';
+  },
+  safe_mode: function(){
+    return this.get('safe_mode') ? 'Yes' : 'No';
+  },
+  remove: function(){
+    this.destroy({
+      success: function(){
+        Notification("Successfully forgot the Cluster!", true);
+      },
+      error: function(){
+        Notification("Failed to forget the Cluster", false);
+      }
+    });
+  }
+});
+
+var ClusterCollection = Backbone.StreamCollection.extend({
+  url: "/zookeepers/" + CurrentZookeeper + "/cluster/",
+  model: ClusterModel,
+  initialize: function(models, options){
+    this.on('add', function(clusters){
+      if (this.length == 1){
+        $('#clusters .no_children').hide();
+        $('#clusters tbody').append(clusters.view.render().$el);
+      } else {
+        $('#clusters tbody').append(clusters.view.render().$el);
+      }
+    });
+    this.on('remove', function(clusters){
+      if (this.length <= 1){
+        $('#clusters .no_children').show();
+        clusters.view.destroy();
+      } else {
+        clusters.view.destroy();
+      }
+    });
+  }
+});
+
+var ClusterView = Backbone.View.extend({
+  tagName: 'tr',
+  template: JST['templates/environment/cluster'],
+  events:{
+    "click .more-shard-info" : "show_shards",
+    "click .destroy-cluster" : "destroy_cluster"
+  },
+  render: function(){
+    this.$el.attr('data-cluster-id', this.model.get('id')).html(this.template({cluster: this.model}));
+    return this;
+  },
+  show_shards: function(event){
+    new ShardCollection(null, {cluster_id: this.model.get('id')});
+  },
+  destroy_cluster: function(){
+    Confirm_Delete({
+      message: "forget this cluster and its associated shards",
+      confirmed_action: _.bind(this.model.remove, this.model)
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/controller.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/controller.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/controller.js
new file mode 100644
index 0000000..580329c
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/controller.js
@@ -0,0 +1,81 @@
+var ControllerModel = Backbone.Model.extend({
+  initialize: function(){
+    this.view = new ControllerView({model: this});
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  url: function(){
+    return '/blur_controllers/' + this.get('id') + '.json';
+  },
+  remove: function(){
+    if(this.get('controller_status') == 0){
+      this.destroy({
+        success: function(){
+          Notification("Successfully forgot the Controller!", true);
+        },
+        error: function(){
+          Notification("Failed to forget the Controller", false);
+        }
+      });
+    } else {
+      Notification("Cannot forget a Controller that is online!", false);
+    }
+  }
+});
+
+var ControllerCollection = Backbone.StreamCollection.extend({
+  url: "/zookeepers/" + CurrentZookeeper + "/controller/",
+  model: ControllerModel,
+  initialize: function(models, options){
+    this.on('add', function(controller){
+      if (this.length == 1){
+        $('#controllers .no_children').hide();
+        $('#controllers tbody').append(controller.view.render().$el);
+      } else {
+        $('#controllers tbody').append(controller.view.render().$el);
+      }
+    });
+    this.on('remove', function(controller){
+      if (this.length == 1){
+        $('#controllers .no_children').show();
+        controller.view.destroy();
+      } else {
+        controller.view.destroy();
+      }
+    });
+  }
+});
+
+var ControllerView = Backbone.View.extend({
+  tagName: 'tr',
+  template: JST['templates/environment/controller'],
+  events:{
+    "click .destroy-controller" : "destroy_controller"
+  },
+  render: function(){
+    this.$el.attr('data-controller-id', this.model.get('id')).html(this.template({controller: this.model}));
+    this.setRowStatus();
+    return this;
+  },
+  setRowStatus: function(){
+    switch(this.model.get('status'))
+    {
+      case 0:
+        this.$el.attr('class', 'error');
+        return;
+      case 1:
+        this.$el.attr('class', '');
+        return;
+      case 2:
+        this.$el.attr('class', 'warning');
+        return;
+    }
+  },
+  destroy_controller: function(){
+    Confirm_Delete({
+      message: "forget this controller",
+      confirmed_action: _.bind(this.model.remove, this.model)
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/environment.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/environment.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/environment.js
new file mode 100644
index 0000000..19b258e
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/environment.js
@@ -0,0 +1,31 @@
+//= require bootstrap-tooltip
+//= require bootstrap-popover
+//= require_tree .
+
+// Confirmation popup for forgetting parts of the ZK
+var Confirm_Delete = function(options){
+  $().popup({
+      title: "Are you sure?",
+      titleClass: 'title',
+      body: '<div>Are you sure that you want to ' + options.message + '?</div>',
+      btns: {
+        "Remove": {
+          "class": "danger",
+          func: function(){
+            options.confirmed_action();
+            $().closePopup();
+          }
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+          }
+        }
+      }
+    });
+};
+
+$(document).ready(function(){
+  // Start streaming the model on 5 sec intervals
+  new ZookeeperModel().stream(5000);
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/shard.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/shard.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/shard.js
new file mode 100644
index 0000000..dd8ef30
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/shard.js
@@ -0,0 +1,103 @@
+var ShardModel = Backbone.Model.extend({
+  initialize: function(){
+    this.view = new ShardView({model: this});
+  },
+  url: function(){
+    return '/blur_shards/' + this.get('id') + '.json';
+  },
+  status: function(){
+    var statusString = "Shard: " + this.get('node_name');
+    statusString += " | Blur Version: " + this.get('blur_version');
+    statusString += " | Status: " + this.onlineStatus();
+    if (this.get('status') != 1) {
+      statusString += " at " + this.offlineDate();
+    }
+    return statusString;
+  },
+  onlineStatus: function(){
+    switch(this.get('shard_status'))
+    {
+      case 0:
+        return "Offline"
+      case 1:
+        return "Online"
+      case 2:
+        return "Quorum Issue"
+    }
+  },
+  remove: function(){
+    if(this.get('shard_status') == 0){
+      this.destroy({
+        success: function(){
+          Notification("Successfully forgot the Shard!", true);
+        },
+        error: function(){
+          Notification("Failed to forget the Shard", false);
+        }
+      });
+    } else {
+      Notification("Cannot forget a Shard that is online!", false);
+    }
+  },
+  offlineDate: function(){
+    var date = new Date(this.get('updated_at').toLocaleString())
+    var formattedDate = date.getMonth() + '/' + date.getDay() + '/' + date.getFullYear();
+    var formattedTime = date.getHours() + ':' + date.getMinutes();
+    return formattedDate + ' ' + formattedTime;
+  }
+});
+
+var ShardCollection = Backbone.Collection.extend({
+  model: ShardModel,
+  initialize: function(models, options){
+    this.url = Routes.cluster_blur_shards_path(options.cluster_id, {format: 'json'});
+    this.view = new ShardCollectionView({collection: this});
+    this.fetch({
+      success: _.bind(function(){
+        this.view.render();
+      }, this)
+    });
+  },
+  comparator: function(shard){
+    return shard.get('node_name');
+  }
+});
+
+var ShardCollectionView = Backbone.View.extend({
+  tagName: 'ul',
+  className: 'modal-list no-well',
+  render: function(){
+    this.collection.each(_.bind(function(shard){
+      this.$el.append(shard.view.render().$el);
+    }, this));
+    this.show_shards();
+  },
+  show_shards: function(){
+    $().popup({
+      title: "Shards",
+      titleClass: 'title',
+      body: this.$el
+    });
+    this.$el.find('.icon').tooltip();
+  }
+});
+
+var ShardView = Backbone.View.extend({
+  tagName: 'li',
+  template: JST['templates/environment/shard'],
+  events:{
+    "click .icon" : "destroy_shard"
+  },
+  render: function(){
+    errorClass = (this.model.get('shard_status') == 1) ? 'no-error' : 'error';
+    this.$el.attr('class', errorClass);
+    this.$el.html(this.template({shard: this.model}));
+    return this;
+  },
+  destroy_shard: function(){
+    Confirm_Delete({
+      message: "forget this shard",
+      confirmed_action: _.bind(this.model.remove, this.model)
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/zookeeper.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/zookeeper.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/zookeeper.js
new file mode 100644
index 0000000..456d67e
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/environment/zookeeper.js
@@ -0,0 +1,125 @@
+var ZookeeperModel = Backbone.Model.extend({
+  clusters: new ClusterCollection(),
+  controllers: new ControllerCollection(),
+  initialize: function(){
+    this.view = new ZookeeperView({model: this, el: '#zookeeper'});
+    // Whenever a property changes re-render the view
+    this.on('change', function(){
+      this.view.render();
+    });
+    this.initial_load = true;
+  },
+  url: function(){
+    return '/zookeepers/' + this.get('id') + '.json';
+  },
+  parse: function(response){
+    if (this.initial_load){
+      if (response.clusters.length <= 0){
+        this.clusters.view.$el.find('.no_children').show();
+      }
+      if (response.blur_controllers.length <= 0){
+        this.blur_controllers.view.$el.find('.no_children').show();
+      }
+      this.initial_load = false;
+    }
+    this.clusters.update(response.clusters);
+    this.controllers.update(response.blur_controllers);
+
+    delete response.clusters
+    delete response.blur_controllers
+
+    this.set(response);
+  },
+  // Model streaming, fetches on every interval
+  stream: function(interval){
+    var _update = _.bind(function() {
+      this.fetch({
+        url: Routes.zookeeper_path(CurrentZookeeper, {format: 'json'})
+      });
+      window.setTimeout(_update, interval);
+    }, this);
+    _update();
+  },
+  // Destroys the zookeeper on the server side
+  remove: function(){
+    if(this.get('zookeeper_status') == 0){
+      this.destroy({
+        success: function(){
+          window.location = window.location.origin;
+        },
+        error: function(){
+          Notification("Failed to forget the Zookeeper", false);
+        }
+      });
+    } else {
+      Notification("Cannot forget a Zookeeper that is online!", false);
+    }
+  },
+  header: function(){
+    return this.get('name') + " - Zookeeper - " + this.translated_status();
+  },
+  quarum_failed: function(){
+    return this.get('zookeeper_status') == 3
+  },
+  offline_nodes: function(){
+    var allNodes = this.get('url').split(',')
+    var online = this.get('ensemble');
+    var offline = [];
+    for (var i = 0; i < allNodes.length; i++){
+      var node = allNodes[i];
+      if (online.indexOf(node) < 0){
+        offline.push(node)
+      }
+    }
+    return offline;
+  },
+  // The translated status
+  translated_status: function(){
+    switch(this.get('zookeeper_status'))
+    {
+      case 0:
+        return "Offline"
+      case 1:
+        return "Online"
+      case 2:
+        return "Ensemble Warning"
+      case 3:
+        return "Quorum Failure"
+    }
+  },
+  // Determines the class for the state of the zookeeper
+  translated_class: function(){
+    switch(this.get('zookeeper_status'))
+    {
+      case 0:
+        return "btn-danger"
+      case 1:
+        return "btn-success"
+      case 2:
+        return "btn-warning"
+      case 3:
+        return "btn-danger"
+    }
+  }
+});
+
+var ZookeeperView = Backbone.View.extend({
+  events: {
+    "click .destroy-zk" : "destroy_zookeeper"
+  },
+  template: JST['templates/environment/zookeeper'],
+  render: function(){
+    this.$el.html(this.template({zookeeper: this.model}));
+    this.$el.removeClass('btn-danger btn-success btn-warning');
+    this.$el.addClass(this.model.translated_class());
+    this.$('i').tooltip();
+    this.$('span.states').tooltip({placement: 'bottom'});
+    return this;
+  },
+  destroy_zookeeper: function(){
+    Confirm_Delete({
+      message: "forget this zookeeper",
+      confirmed_action: _.bind(this.model.remove, this.model)
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b5b86c7e/src/contrib/blur-console/blur-admin/app/assets/javascripts/hdfs.js
----------------------------------------------------------------------
diff --git a/src/contrib/blur-console/blur-admin/app/assets/javascripts/hdfs.js b/src/contrib/blur-console/blur-admin/app/assets/javascripts/hdfs.js
new file mode 100644
index 0000000..31afdfa
--- /dev/null
+++ b/src/contrib/blur-console/blur-admin/app/assets/javascripts/hdfs.js
@@ -0,0 +1,921 @@
+//= require jquery.contextMenu
+//= require jquery.osxFinder
+//= require d3/d3
+//= require flot/flot
+//= require_self
+
+$(document).ready(function() {
+  //document varss
+  var delete_file, draw_radial_graph, finishUploading, make_dir, navigateUsingPath, paste_buffer, rightClicked,
+    perform_action, reload_hdfs, show_dir_props, show_hdfs_props, pre_cut_file, cut_file, upload, uploadFailed, in_file = [],
+    allSelected = [], columnSelected = [], lastClicked, ctrlHeld = false, shiftHeld = false, historyUndef = false;
+
+  //TODO: figure out why this doesn't work
+  // Old browser support for history push state
+  if (typeof history.pushState === 'undefined') {
+    history.pushState = function() {
+      historyUndef = true;
+    };
+  }
+
+  // One time use page variable initialization
+  (function() {
+    var headerHeight = 0;
+    var footerHeight = 0;
+    window.onload = function() {
+      headerHeight = parseInt($('.navbar').css('height'), 10);
+      footerHeight = parseInt($('#ft').css('height'), 15);
+      var newHeight = window.innerHeight - (footerHeight + headerHeight) - 20;
+      $('#hdfs_wrapper').animate({height: newHeight + 'px'}, 400);
+    };
+    $(window).resize(function() {
+      $('#hdfs_wrapper').css('height', window.innerHeight - (footerHeight + headerHeight) - 15);
+    });
+  })();
+
+  /*
+   * Page Widget Setup Methods
+  */
+
+  var setup_context_menus = function() {
+    $('#hdfs_browser li.hdfs_instance').contextMenu(
+      { menu: 'hdfs-root-context-menu' },
+      function(action, el, pos) {
+        perform_action(action, el);
+        return false;
+      }
+    );
+    $('#hdfs_browser li.folder').contextMenu(
+      { menu: 'hdfs-dir-context-menu' },
+      function(action, el, pos) {
+        perform_action(action, el);
+        return false;
+      }
+    );
+    $('#hdfs_browser li.file').contextMenu(
+      {menu: 'hdfs-file-context-menu'},
+      function(action, el, pos) {
+        perform_action(action, el);
+        return false;
+      }
+    );
+    $('#hdfs_browser .innerWindow').contextMenu(
+      {menu: 'hdfs-whitespace-context-menu'},
+      function(action, el, pos) {
+        var prev = el.prev().find('.osxSelected');
+        perform_action(action, prev);
+        return false;
+      }
+    );
+    $('#hdfs-dir-context-menu, #hdfs-whitespace-context-menu').disableContextMenuItems('#paste');
+    if ($('#hdfs_browser').attr('hdfs_editor') === 'false') {
+      $('.contextMenu').disableContextMenuItems('#paste,#mkdir,#cut,#rename,#delete');
+    }
+  };
+
+  var tree_context_menu = function() {
+    return $(["<div class='context_menus'>",
+        "<ul id='hdfs-root-context-menu' class='contextMenu'>",
+          "<li class='mkdir'><a href='#mkdir'>New Folder</a></li>",
+          "<li class='edit'><a href='#upload'>Upload File</a></li>",
+          "<li class='props separator'><a href='#props'>Properties</a></li>",
+        "</ul>",
+        "<ul id='hdfs-dir-context-menu' class='contextMenu'>",
+          "<li class='mkdir'><a href='#mkdir'>New Folder</a></li>",
+          "<li class='edit'><a href='#upload'>Upload File</a></li>",
+          "<li class='rename'><a href='#rename'>Rename</a></li>",
+          "<li class='cut'><a href='#cut'>Cut</a></li>",
+          "<li class='paste'><a href='#paste'>Paste</a></li>",
+          "<li class='delete'><a href='#delete'>Delete</a></li>",
+          "<li class='props separator'><a href='#dirprops'>Properties</a></li>",
+        "</ul>",
+        "<ul id='hdfs-file-context-menu' class='contextMenu'>",
+          "<li class='rename'><a href='#rename'>Rename</a></li>",
+          "<li class='cut'><a href='#cut'>Cut</a></li>",
+          "<li class='delete'><a href='#delete'>Delete</a></li>",
+          "<li class='props separator'><a href='#dirprops'>Properties</a></li>",
+        "</ul>",
+        "<ul id='hdfs-whitespace-context-menu' class='contextMenu'>",
+          "<li class='mkdir'><a href='#mkdir'>New Folder</a></li>",
+          "<li class='edit'><a href='#upload'>Upload File</a></li>",
+          "<li class='paste'><a href='#paste'>Paste</a></li>",
+          "<li class='props separator'><a href='#dirprops'>Properties</a></li>",
+        "</ul>",
+      "</div>"].join('\n'));
+  };
+
+  var draw_radial_graph = function(width, height, json) {
+    var showGraphTooltip = function(graph, tipContent) {
+      var tooltip = $('<div class="graphtip" ><div id="tipcontent">' + tipContent + '</div></div>');
+      $('.radial-graph').append(tooltip);
+      var graphWidth = graph.outerWidth();
+      var graphHeight = graph.outerHeight();
+      var tipWidth = tooltip.outerWidth();
+      var tipHeight = tooltip.outerHeight();
+      var drawPositionX = (graphWidth / 2) - (tipWidth / 2);
+      var drawPositionY = (graphHeight / 2) - (tipHeight / 2);
+      tooltip.css({
+        top: drawPositionY + 'px',
+        left: drawPositionX + 'px'
+      });
+      tooltip.fadeIn(400);
+    };
+    var radius = Math.min(width, height) / 2;
+    var color = d3.scale.category20c();
+    var selector = ".radial-graph";
+    $(selector).empty();
+    var vis = d3.select(selector).append("svg").attr("width", width).attr("height", height).append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
+    var partition = d3.layout.partition().sort(function(a, b) {
+      return b.size - a.size;
+    }).size([2 * Math.PI, radius * radius]).value(function(d) {
+      return d.size;
+    });
+    var arc = d3.svg.arc().startAngle(function(d) {
+      return d.x;
+    }).endAngle(function(d) {
+      return d.x + d.dx;
+    }).innerRadius(function(d) {
+      return Math.sqrt(d.y);
+    }).outerRadius(function(d) {
+      return Math.sqrt(d.y + d.dy);
+    });
+    var path = vis.data([json]).selectAll("path").data(partition.nodes).enter().append("path").attr("display", function(d) {
+      if (d.depth) {
+        return null;
+      } else {
+        return "none";
+      }
+    }).attr("d", arc).attr("fill-rule", "evenodd").style("stroke", "#fff").style("fill", function(d) {
+      return color((d.children ? d : d.parent).name);
+    }).attr("title", function(d) {
+      return d.name;
+    });
+    var timeoutShowVar = null;
+    $('path').hover(function() {
+      var title = $(this).attr('title') || "No path name found!";
+      $('.graphtip').remove();
+      clearTimeout(timeoutShowVar);
+      timeoutShowVar = setTimeout((function() {
+        showGraphTooltip($('.radial-graph'), title);
+      }), 500);
+    }, function() {
+      clearTimeout(timeoutShowVar);
+      $('.graphtip').remove();
+    });
+  };
+
+  /*
+   * HDFS Actions
+  */
+
+  var pre_cut_file = function(action, el) {
+    if (paste_buffer.multiple) {
+      $.each(paste_buffer.multiple, function(index, value) {
+        $(value).removeClass('to-cut');
+      });
+    }
+    if (paste_buffer.location) {
+      $(paste_buffer.location).removeClass('to-cut');
+    }
+    paste_buffer.location = el;
+    paste_buffer.action = action;
+    paste_buffer.multiple = columnSelected;
+    $('#hdfs-dir-context-menu, #hdfs-whitespace-context-menu').enableContextMenuItems('#paste');
+    if (paste_buffer.multiple.length > 0) {
+      $.each(paste_buffer.multiple, function(index, value) {
+        $(value).addClass('to-cut');
+      });
+    }
+    else {
+      $(paste_buffer.location).addClass('to-cut');
+    }
+  };
+
+  var cut_file = function(file, location) {
+    var from_id = file.attr('hdfs_id');
+    var from_path = file.attr('hdfs_path');
+    var to_id = location.attr('hdfs_id');
+    var to_path = location.attr('hdfs_path');
+    if (from_id === to_id) {
+      $.ajax({
+        url: Routes.move_hdfs_path(to_id, {format: 'json'}),
+        type: 'POST',
+        data: {
+          from: from_path,
+          to: to_path
+        },
+        success: function(){
+          $('#hdfs-dir-context-menu, #hdfs-whitespace-context-menu').disableContextMenuItems('#paste');
+          if (!historyUndef) {
+            reload_hdfs();
+          }
+          else {
+            reload_hdfs_ff(to_path, to_id);
+          }
+        },
+        error: function(xhr, status, error){
+          console.log(status + "  " + error);
+        }
+      });
+    }
+  };
+
+  var rename = function(el) {
+    var id = el.attr('hdfs_id');
+    var from_path = el.attr('hdfs_path');
+    $('<div id="newName"><lable>New name for '+ from_path.split('/').pop() +'</label><br/><br/><input></input></div>').popup({
+      title: 'New Name',
+      titleClass: 'title',
+      shown: function() {
+        $('#newName input').focus();
+      },
+      onEnter: true,
+      btns: {
+        "Create": {
+          "class": "primary",
+          func: function() {
+            var newName = $('#newName input').val();
+            var newFullPath = "" + (from_path.substring(0, from_path.lastIndexOf('/') + 1)) + newName;
+            var unique = true;
+            $.each(el.siblings(), function(index, value){
+              if(newFullPath == $(value).attr('hdfs_path')) unique = false;
+            });
+            if (newName == '') {
+              $().closePopup();
+              errorPopup("Name was not entered.");
+            }
+            else if (!unique){
+              $().closePopup();
+              errorPopup("Name already in use.");
+            }
+            else{
+              $.ajax(Routes.move_hdfs_path(id, {format: 'json'}), {
+                type: 'post',
+                data: {
+                  from: from_path,
+                  to: newFullPath
+                },
+                success: function() {
+                  el.attr('hdfs_path', newFullPath);
+                  var link = el.find('a');
+                  link.html(newName);
+                  var href = link.attr('href');
+                  link.attr('href', href.replace(from_path, newFullPath));
+                  if (el.hasClass('osxSelected')) {
+                    var nextWin = el.parents('.innerWindow').next();
+                    var display_href = el.find('a').attr('href');
+                    nextWin.load(display_href);
+                  } else {
+                    el.click();
+                  }
+                }
+              });
+              $().closePopup();
+            }
+          }
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+          }
+        }
+      }
+    });
+  };
+
+  var delete_file = function(file) {
+    var id = file.attr('hdfs_id');
+    var path = file.attr('hdfs_path');
+    $.post(Routes.delete_file_hdfs_path(id, {format: 'json'}), {
+      'path': path
+    }, function() {
+      if (!historyUndef) {
+        reload_hdfs();
+      }
+      else {
+        reload_hdfs_ff(path, id);
+      }
+    });
+  };
+
+  var delete_additional_files = function(clicked_file) {
+    $.each(columnSelected, function(index, value){
+      if(!(clicked_file[0] == value)) {
+        delete_file($(value));
+      }
+    });
+  };
+
+  //Drag and Drop Functionality
+  //Event listener functions for drag and drop
+   function dragEnter (evt){
+    $(this).addClass('currentDrop');
+    evt.stopPropagation();
+    evt.preventDefault();
+  }
+  function dragOver (evt){
+    evt.stopPropagation();
+    evt.preventDefault();
+  }
+  function dragExit(evt){
+    $(this).removeClass('currentDrop');
+    evt.stopPropagation();
+    evt.preventDefault();
+  }
+  function drop(evt){
+    evt.stopPropagation();
+    evt.preventDefault();
+    var files = evt.originalEvent.dataTransfer.files; //FileList object
+    var count = files.length;
+    if (count > 0)
+      handleFiles(files);
+  }
+
+  //Compatability check
+if (window.File && window.FileReader && window.FileList && window.Blob){
+
+  $('#hdfs_browser').on('dragenter', '.innerWindow', dragEnter);
+  $('#hdfs_browser').on('dragleave', '.innerWindow', dragExit);
+  $('#hdfs_browser').on('dragover', '.innerWindow', dragOver);
+  $('#hdfs_browser').on('drop', '.innerWindow', drop);
+
+}
+//Posts the form data to the controller
+var handleFiles = function(files) {
+  var id =$('#top_level .osxSelected').attr('hdfs_id');
+  var target_path = $('.currentDrop').prev().find('.osxSelected').attr('hdfs_path');
+  var file = files[0];
+  if (file.size > 26214400){
+    console.error("File was too large. Needs to be less than 25 MB");
+  }
+
+  var formData = new FormData();
+  formData.append("upload", file);
+  formData.append("path", target_path);
+  formData.append("id", id);
+  var xhr = new XMLHttpRequest();
+  xhr.open('POST', Routes.upload_hdfs_path(id), true);
+  xhr.onload = function(e){
+    if (this.status != 200)
+      console.log("Loaded " + file.name);
+  };
+  xhr.send(formData);
+  if (!historyUndef) {
+    reload_hdfs();
+  }
+  else {
+    reload_hdfs_ff(target_path, id);
+  }
+};
+//End of DnD Functionality
+
+  var upload = function(el) {
+    var id = el.attr('hdfs_id');
+    var path = el.attr('hdfs_path');
+    var modal_container = $('<div id="upload_form_modal_container"></div>');
+    in_file = [];
+    $('.osxSelectable[hdfs_path="' + path + '"][hdfs_id=' + id + ']').click();
+    var osxWindow = ( path == '/' ? 1 : path.split('/').length );
+
+    modal_container.load(Routes.upload_form_hdfs_path(id), function(data) {
+      $().popup({
+        body: data,
+        title: 'Upload File',
+        titleClass: 'title',
+        show: function() {
+          $('#fpath-input').val(path);
+          $('#hdfs-id-input').val(id);
+          $.each( $($('.innerWindow')[osxWindow]).find('a'), function (index, value){
+            in_file.push($(value).attr('title'));
+          });
+          $('input[type=file]').change( function(event) {
+            if (in_file.indexOf($('#file-input').val().split('\\').pop()) < 0) {
+              $('#upload_file_warning').addClass('hidden');
+            }
+            else {
+              $('#upload_file_warning').removeClass('hidden');
+            }
+          });
+        },
+        hide: function() {
+          !window.uploading;
+        },
+        btns: {
+          "Upload": {
+            "class": "primary",
+            func: function() {
+              $('#upload-form').submit();
+            }
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    });
+  };
+
+  var make_dir = function(el) {
+    var id = el.attr('hdfs_id');
+    var path = el.attr('hdfs_path');
+    in_file = [];
+    $('.osxSelectable[hdfs_path="' + path + '"][hdfs_id=' + id + ']').click();
+    var osxWindow = ( path == '/' ? 1 : path.split('/').length );
+
+    $('<div id="newFolder"><label>Folder Name:</label><input></input><br/><br/>Parent directory: '+ path +'</div>').popup({
+      title: 'New Folder',
+      titleClass: 'title',
+      shown: function() {
+        $('#newFolder input').focus();
+      },
+      onEnter: true,
+      btns: {
+        "Create": {
+          "class": 'primary',
+          func: function() {
+            var newName = $('#newFolder input').val();
+            var unique = true;
+
+            $.each( $($('.innerWindow')[osxWindow]).find('a'), function (index, value) {
+              in_file.push($(value).attr('title'));
+            });
+
+            $.each(in_file, function(index, value) {
+              if(newName == value) unique = false;
+            });
+
+            if (newName == '') {
+              $().closePopup();
+              errorPopup("Name was not entered.");
+            }
+            else if (!unique) {
+              $().closePopup();
+              errorPopup("Folder or file with this name already in use.");
+            }
+            else {
+              $.ajax(Routes.mkdir_hdfs_path(id, {format: 'json'}), {
+                type: 'post',
+                data: {
+                  fs_path: path,
+                  folder: $('#newFolder input').val()
+                },
+                success: function() {
+                  var display_href, nextWin;
+                  if (el.hasClass('osxSelected')) {
+                    nextWin = el.parents('.innerWindow').next();
+                    display_href = el.find('a').attr('href');
+                    nextWin.load(display_href);
+                  } else {
+                    el.click();
+                  }
+                }
+              });
+            $().closePopup();
+            }
+          }
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+          }
+        }
+      }
+    });
+  };
+
+  var show_hdfs_props = function(id, name) {
+    var title = "HDFS Information (" + name + ")";
+    $('.hdfs_instance[hdfs_id=' + id + ']').click();
+    $.get(Routes.info_hdfs_path(id), function(data) {
+      $(data).popup({
+        title: title,
+        titleClass: 'title',
+        show: function() {
+          $.get(Routes.structure_hdfs_path(id, {format: 'json'}), {
+            'fs_path': '/'
+          }, function(data) {
+            draw_radial_graph(520, 400, data);
+          });
+          $('#modal').css({
+            'width': '1120px',
+            'margin-left': '-560px'
+          });
+          $('.modal-footer').css({
+            'width': '1090px'
+          });
+        }
+      });
+    });
+  };
+
+  var show_dir_props = function(id, path) {
+    var title = "Properties for " + path;
+    $('.osxSelectable[hdfs_path="' + path + '"][hdfs_id=' + id + ']').click();
+    $.get(Routes.folder_info_hdfs_path(id), {
+      'fs_path': path
+    }, function(data) {
+      $(data).popup({
+        titleClass: 'title',
+        title: title,
+        show: function() {
+          $.get(Routes.slow_folder_info_hdfs_path(id, {format: 'json'}), {
+            'fs_path': path
+          }, function(data) {
+            $('#file_count').html(data.file_count);
+            $('#folder_count').html(data.folder_count);
+            $('#file_size').html(data.file_size);
+          });
+          $.get(Routes.structure_hdfs_path(id, {format: 'json'}), {
+            'fs_path': path
+          }, function(data) {
+            draw_radial_graph(520, 400, data);
+          });
+          $('#modal').css({
+            'width': '1120px',
+            'margin-left': '-560px'
+          });
+          $('.modal-footer').css({
+            'width': '1090px'
+          });
+        }
+      });
+    });
+  };
+
+  var perform_action = function(action, el) {
+    switch (action) {
+      case "delete":
+        if (columnSelected.length > 0) {
+          confirmPopup("Are you sure you wish to delete these files? This action can not be undone.", function() {
+            delete_additional_files(el);
+            delete_file(el);//need both - delete_file catches the clicked element when its not highlighted
+          });
+        }
+        else {
+          confirmPopup("Are you sure you wish to delete " + el.attr('hdfs_path') + "? This action can not be undone.", function() {
+            delete_file(el);
+          });
+        }
+        break;
+      case "cut":
+        pre_cut_file(action, el);
+        break;
+      case "paste":
+        if (paste_buffer.action && paste_buffer.action === "cut") {
+          if (paste_buffer.multiple.length > 0){
+            $.each(paste_buffer.multiple, function(index, value){
+              cut_file($(value), el);
+            });
+          }
+          else {
+            cut_file(paste_buffer.location, el);
+          }
+        }
+        break;
+      case "props":
+        show_hdfs_props(el.attr('hdfs_id'), el.attr('hdfs_name'));
+        break;
+      case "dirprops":
+        show_dir_props(el.attr('hdfs_id'), el.attr('hdfs_path'));
+        break;
+      case "mkdir":
+        make_dir(el);
+        break;
+      case "rename":
+        rename(el);
+        break;
+      case "upload":
+        upload(el);
+        break;
+    }
+  };
+
+  /*
+   * Upload Methods
+  */
+
+  // Upload methods on the window so returned JS can call them
+  window.finishUploading = function(path) {
+    $("li[hdfs_path='" + path + "']").click();
+    $().closePopup();
+    window.uploading = false;
+    if (!historyUndef) {
+      reload_hdfs();
+    }
+    else {
+      reload_hdfs_ff(path, $('.osxSelected').attr('hdfs_id'));
+    }
+  };
+
+  window.uploadFailed = function(error) {
+    errorPopup(error);
+    window.uploading = false;
+  };
+
+  $('#upload-form').live('submit', function() {
+    window.uploading = true;
+    $('#upload-file #status').html('<h2>Uploading...</h2>');
+    $('#upload-file #upload-button').attr('disabled', 'disabled');
+  });
+
+  reload_hdfs = function() {
+    $('.osxSelected').removeClass('osxSelected');
+    navigateUsingPath();
+  };
+
+  reload_hdfs_ff = function(path, hdfsId) { //fix for FF 3.6
+    $('.osxSelected').removeClass('osxSelected');
+    navigateOsxWindow(path, hdfsId);
+  }
+
+  /*
+    Methods for HTML History manipulation
+  */
+
+  navigateUsingPath = function() {
+    var pathPieces = window.location.pathname.split('/').filter(function(member) {
+      return member !== '';
+    }).slice(1);
+    var hdfsId = pathPieces.shift();
+    var path = '/' + pathPieces.slice(1).join('/');
+    $('#hdfs_browser').osxFinder('navigateToPath', path, hdfsId, true);
+    $('.innerWindow').first().disableContextMenu();
+  };
+  window.onpopstate = function(e) {
+    navigateUsingPath();
+  };
+  navigateOsxWindow = function(path, hdfsId) {
+    $('#hdfs_browser').osxFinder('navigateToPath', path, hdfsId, true);
+    $('.innerWindow').first().disableContextMenu();
+  };
+
+  /*
+   * Methods for modals
+  */
+
+  errorPopup = function(message) {
+    $('<div id="error">' + message +'</div>').popup({
+      title: 'Error',
+      shown: function() {
+        $('.btn-primary').focus();
+      },
+      btns: {
+        "Ok": {
+          "class": "primary",
+          func: function(){
+            $().closePopup();
+          }
+        }
+      }
+    });
+  };
+
+  confirmPopup = function(message, confirmFnct) {
+    $('<div id="confirm">' + message +'</div>').popup({
+      title: 'Confirm',
+      shown: function() {
+        $('.btn-primary').focus();
+      },
+      btns: {
+        "Ok": {
+          "class": "primary",
+          func: function() {
+            $().closePopup();
+            confirmFnct();
+          }
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+            return false;
+          }
+        }
+      }
+    });
+  };
+
+  /*
+    Methods to call on page load
+  */
+
+  $(document.body).append(tree_context_menu());
+  setup_context_menus();
+  paste_buffer = {};
+  $('path').live('click', function() {
+    $().closePopup();
+    var id = $('#top_level .osxSelected').attr('hdfs_id');
+    var fullpath = $(this).attr('title');
+    fullpath = fullpath.substring(fullpath.indexOf('//') + 2);
+    var path = fullpath.substring(fullpath.indexOf('/'));
+    $('#hdfs_browser').osxFinder('navigateToPath', path);
+    show_dir_props(id, path);
+  });
+  $('#hdfs_browser').osxFinder({
+    done: function() {
+      navigateUsingPath();
+    },
+    navigated: function(e, data) {
+      if (history.pushState) {
+        history.pushState({}, '', data.url);
+      }
+    }
+  });
+
+  /*
+   * Multiple select
+  */
+
+  //de-selects everything stored in columnSelected except for keep_selected
+  var remove_selected = function(keep_selected){
+    $.each(columnSelected, function(index, value){
+      if (value != keep_selected){
+        $(value).removeClass('osxSelected');
+      }
+    });
+  };
+
+  //selects everything in the group_selected, de-selects current if already selected
+  var add_selected = function(group_selected, current){
+    $.each(group_selected, function(index, value){
+      if (value == current && group_selected.length > 1 && !shiftHeld)
+        $(value).removeClass('osxSelected');
+      else
+        $(value).addClass('osxSelected');
+    });
+  };
+
+  //select the item that was originally selected in the clicked column
+  var add_last_selected = function(parent) {
+    $.each(allSelected, function(index, value) {
+      if ($(value).parent()[0] == parent[0]){
+        $(value).addClass('osxSelected');
+        lastClicked = value;
+      }
+    });
+  };
+
+  //disables or enables items in context menus
+  var checkContextMenus = function() {
+    if (columnSelected.length > 1) {
+      $('.contextMenu').disableContextMenuItems('#mkdir,#upload,#rename,#dirprops');
+    }
+    else {
+      $('.contextMenu').enableContextMenuItems('#mkdir,#upload,#rename,#dirprops');
+    }
+  };
+
+  //method for shift multiple select with Ctrl
+  var shiftCtrlSelect = function (parent, elems) {
+    if (columnSelected.length > 1 && elems[0] != lastClicked) {
+      var sibs = parent.children();
+      var inside = false;
+      $.each(sibs, function(index, value){
+        if ($(value).attr('hdfs_path') == elems.attr('hdfs_path')
+            || $(value).attr('hdfs_path') == $(lastClicked).attr('hdfs_path')) {
+          inside = (inside ? false : true);
+        }
+        else if (inside){
+          $(value).addClass('osxSelected');
+        }
+      });
+      columnSelected = $(parent).find('.osxSelected');
+    }
+  };
+
+  //method for shift multiple select without Ctrl
+  var shiftSelect = function (parent, elems) {
+    if (columnSelected.length == 1 && $(lastClicked).parent()[0] == parent[0]) {
+      $(lastClicked).addClass('osxSelected');
+      columnSelected = $(parent).find('.osxSelected');
+    }
+    if (columnSelected.length > 1) {
+      var inside = false;
+      var elemsPath = elems.attr('hdfs_path');
+      var colPath = $(columnSelected[0]).attr('hdfs_path');
+      var start = (elemsPath < colPath ? elemsPath : colPath);
+      var end = (start == elemsPath ? $(columnSelected[columnSelected.length-1]).attr('hdfs_path') : elemsPath);
+
+      $.each($(parent).children(), function(index, value){
+        if (!inside && $(value).attr('hdfs_path') == start) {
+          inside = true;
+          $(value).addClass('osxSelected');
+        }
+        else if (inside && $(value).attr('hdfs_path') == end) {
+          inside = false;
+          $(value).addClass('osxSelected');
+        }
+        else if (inside){
+          $(value).addClass('osxSelected');
+        }
+        else {
+          $(value).removeClass('osxSelected');
+        }
+      });
+      columnSelected = $(parent).find('.osxSelected');
+    }
+  };
+
+  $(document).on('click', function(e){
+    if(e.which == 1){ //checks for left mouse button (needed in FF 3.6)
+      $(rightClicked).removeClass('rclicked');
+      var elems = $(e.target).closest('li');
+
+      //click outside of the lists
+      if (elems.length == 0 && !ctrlHeld) {
+        remove_selected(lastClicked);
+        columnSelected = [];
+        lastClicked = elems[0];
+        $('.contextMenu').enableContextMenuItems('#mkdir,#upload,#rename,#dirprops');
+      }
+
+      //click a list element
+      else {
+        var parent = elems.parent();
+
+        if(ctrlHeld) { //CTRL held down
+          if (columnSelected.length == 0) {
+            add_last_selected(parent);
+          }
+          else if ($(columnSelected[0]).parent()[0] == parent[0]) {
+            add_selected(columnSelected, elems[0]);
+          }
+          else {
+            remove_selected(lastClicked);
+          }
+
+          columnSelected = $(parent).find('.osxSelected');
+          if (shiftHeld) {
+            shiftCtrlSelect(parent, elems);
+          }
+          lastClicked = ( columnSelected.length > 0 ? elems[0] : null);
+        }
+
+        else { //CTRL not held down
+          if (shiftHeld) {
+            if (columnSelected.length == 0) {
+              add_last_selected(parent);
+              columnSelected = $(parent).find('.osxSelected');
+              shiftSelect(parent, elems);
+            }
+            else if ($(columnSelected[0]).parent()[0] == parent[0]) {
+              shiftSelect(parent, elems);
+            }
+            else {
+              remove_selected(lastClicked);
+              columnSelected = $(parent).find('.osxSelected');
+            }
+            lastClicked = elems[0];
+          }
+
+          else {
+            remove_selected(elems[0]);
+            if ($(columnSelected[0]).parent()[0] != parent[0]) {
+              $(lastClicked).addClass('osxSelected');
+            }
+            columnSelected = [];
+            $('.contextMenu').enableContextMenuItems('#mkdir,#upload,#rename,#dirprop');
+            lastClicked = null;
+          }
+        }
+        checkContextMenus();
+      }
+      if (columnSelected.length > 1) {
+        $('.innerWindow').disableContextMenu();
+      }
+      else {
+        $('.innerWindow').enableContextMenu();
+        $('.innerWindow').first().disableContextMenu();
+      }
+    }if(e.which == 3){ //to account for the right click
+      $(rightClicked).removeClass('rclicked');
+      rightClicked = $(e.target).closest('li');
+      $(rightClicked).addClass('rclicked');
+    }
+    else {
+      //NOTE: this only works for FF 3.6
+      //TODO: fix for Chrome (this listener is not picking up right click in Chrome)
+      lastWindow = $('.innerWindow').last();
+      if (lastWindow.find('#file_table').length > 0) {
+        lastWindow.disableContextMenu();
+      }
+    }
+  });
+
+  // Method for holding down CTRL key
+  $(document).on('keydown', function(e) {
+    shiftHeld = e.shiftKey;
+    ctrlHeld = (e.ctrlKey || e.which == 224);
+    if (shiftHeld || ctrlHeld) {
+      allSelected = $('#hdfs_browser').find('.osxSelected');
+    }
+  });
+
+  // Method when CTRL key is released
+  $(document).on('keyup', function(e) {
+    ctrlHeld = (e.ctrlKey  || e.which == 224);
+    shiftHeld = e.shiftKey;
+  });
+
+});


Mime
View raw message