Browse Source

linting and added filter states

Rashid Khan 12 years ago
parent
commit
c03373c692

+ 7 - 7
js/app.js

@@ -12,9 +12,9 @@ var modules = [
   '$strap.directives',
   'kibana.panels',
   'ngSanitize',
-  ]
+];
 
-var scripts = []
+var scripts = [];
 
 var labjs = $LAB
   .script("common/lib/jquery-1.8.0.min.js").wait()
@@ -34,12 +34,12 @@ var labjs = $LAB
   .script("js/controllers.js")
   .script("js/filters.js")
   .script("js/directives.js")
-  .script("js/panels.js").wait()
+  .script("js/panels.js").wait();
 
 _.each(config.modules, function(v) {
-  labjs = labjs.script('panels/'+v+'/module.js')
-  modules.push('kibana.'+v)
-})
+  labjs = labjs.script('panels/'+v+'/module.js');
+  modules.push('kibana.'+v);
+});
 
 /* Application level module which depends on filters, controllers, and services */
 labjs.wait(function(){
@@ -59,7 +59,7 @@ labjs.wait(function(){
         });
     }]);
   angular.element(document).ready(function() {
-    $('body').attr('ng-controller', 'DashCtrl')
+    $('body').attr('ng-controller', 'DashCtrl');
     angular.bootstrap(document, ['kibana']);
   });
 });

+ 31 - 26
js/controllers.js

@@ -11,7 +11,7 @@ angular.module('kibana.controllers', [])
     editable: true,
     rows: [],
     last: null
-  }
+  };
 
   $scope.init = function() {
 
@@ -22,16 +22,16 @@ angular.module('kibana.controllers', [])
     $scope.dashboard = dashboard;
 
     // Provide a global list of all see fields
-    $scope.fields = fields
+    $scope.fields = fields;
     $scope.reset_row();
     $scope.clear_all_alerts();
 
     var ejs = $scope.ejs = ejsResource(config.elasticsearch);  
-  }
+  };
 
   $scope.add_row = function(dash,row) {
     dash.rows.push(row);
-  }
+  };
 
   $scope.reset_row = function() {
     $scope.row = {
@@ -42,8 +42,8 @@ angular.module('kibana.controllers', [])
   };
 
   $scope.row_style = function(row) {
-    return { 'min-height': row.collapse ? '5px' : row.height }
-  }
+    return { 'min-height': row.collapse ? '5px' : row.height };
+  };
 
   $scope.alert = function(title,text,severity,timeout) {
     var alert = {
@@ -52,30 +52,34 @@ angular.module('kibana.controllers', [])
       severity: severity || 'info',
     };
     $scope.global_alert.push(alert);
-    if (timeout > 0)
+    if (timeout > 0) {
       $timeout(function() {
-        $scope.global_alert = _.without($scope.global_alert,alert)
+        $scope.global_alert = _.without($scope.global_alert,alert);
       }, timeout);
-  }
+    }
+  };
 
   $scope.clear_alert = function(alert) {
     $scope.global_alert = _.without($scope.global_alert,alert);
-  }
+  };
 
   $scope.clear_all_alerts = function() {
-    $scope.global_alert = []
-  }  
+    $scope.global_alert = [];
+  }; 
 
   $scope.edit_path = function(type) {
-    if(type)
+    if(type) {
       return 'panels/'+type+'/editor.html';
-  }
+    } else {
+      return false;
+    }
+  };
 
   // This is whoafully incomplete, but will do for now 
   $scope.parse_error = function(data) {
-    var _error = data.match("nested: (.*?);")
+    var _error = data.match("nested: (.*?);");
     return _.isNull(_error) ? data : _error[1];
-  }
+  };
 
   $scope.init();
 
@@ -89,31 +93,32 @@ angular.module('kibana.controllers', [])
     collapsable: true,
     editable: true,
     panels: [],
-  }
-  _.defaults($scope.row,_d)
+  };
+
+  _.defaults($scope.row,_d);
 
 
   $scope.init = function() {
     $scope.reset_panel();
-  }
+  };
 
   $scope.toggle_row = function(row) {
     row.collapse = row.collapse ? false : true;
     if (!row.collapse) {
       $timeout(function() {
-        $scope.$broadcast('render')
+        $scope.$broadcast('render');
       });
     }
-  }
+  };
 
-  // This can be overridden by individual panel
+  // This can be overridden by individual panels
   $scope.close_edit = function() {
-    $scope.$broadcast('render')
-  }
+    $scope.$broadcast('render');
+  };
 
   $scope.add_panel = function(row,panel) {
     $scope.row.panels.push(panel);
-  }
+  };
 
   $scope.reset_panel = function() {
     $scope.panel = {
@@ -121,7 +126,7 @@ angular.module('kibana.controllers', [])
       error   : false,
       span    : 3,
       editable: true,
-      group   : ['default'],
+      group   : ['default']
     };
   };
 

+ 10 - 7
js/directives.js

@@ -24,7 +24,7 @@ angular.module('kibana.directives', [])
           var template = '<div ng-controller="'+scope.panel.type+'" ng-include src="\''+scope.edit_path(scope.panel.type)+'\'"></div>';
           elem.html($compile(angular.element(template))(scope));
         }
-      })
+      });
     }
   };
 })
@@ -39,11 +39,13 @@ angular.module('kibana.directives', [])
       }
 
       function join_array(text) {
-        if(_.isArray(text))  
+        if(_.isArray(text)) {
           return (text || '').join(',');
-        else
-          return text
+        } else {
+          return text;
+        }
       }
+
       ngModel.$parsers.push(split_array);
       ngModel.$formatters.push(join_array);
     }
@@ -54,8 +56,10 @@ angular.module('kibana.directives', [])
     restrict: 'A',
     require: 'ngModel',
     link: function(scope, elm, attr, ngModelCtrl) {
-      if (attr.type === 'radio' || attr.type === 'checkbox') return;
-      
+      if (attr.type === 'radio' || attr.type === 'checkbox') {
+        return;
+      }
+
       elm.unbind('input').unbind('keydown').unbind('change');
       elm.bind('blur', function() {
         scope.$apply(function() {
@@ -65,5 +69,4 @@ angular.module('kibana.directives', [])
     }
   };
 });
-;
 

+ 1 - 1
js/filters.js

@@ -6,5 +6,5 @@ angular.module('kibana.filters', [])
 .filter('stringSort', function() {
     return function(input) {
       return input.sort();
-    }
+    };
   });

+ 1 - 1
js/panels.js

@@ -2,4 +2,4 @@
 /*global angular:true */
 'use strict';
 
-angular.module('kibana.panels', [])
+angular.module('kibana.panels', []);

+ 182 - 184
js/services.js

@@ -1,16 +1,18 @@
 /*jshint globalstrict:true */
 /*global angular:true */
+/*global Blob:false*/
 'use strict';
 
 angular.module('kibana.services', [])
 .service('eventBus', function($rootScope) {
 
   // An array of registed types
-  var _types = []
+  var _types = [];
 
   this.broadcast = function(from,to,type,data) {
-    if(_.isUndefined(data))
-      var data = from
+    if(_.isUndefined(data)) {
+      data = from;
+    }
 
     var packet = {
       time: new Date(),
@@ -18,38 +20,41 @@ angular.module('kibana.services', [])
       from: from,
       to: to,
       data: data
-    }
+    };
 
-    if(_.contains(_types,'$kibana_debug'))
+    if(_.contains(_types,'$kibana_debug')) {
       $rootScope.$broadcast('$kibana_debug',packet);
+    }
 
     $rootScope.$broadcast(type,{
       from: from,
       to: to,
       data: data
     });
-  }
+  };
 
   // This sets up an $on listener that checks to see if the event (packet) is
   // addressed to the scope in question and runs the registered function if it
   // is.
   this.register = function(scope,type,fn) {
 
-    _types = _.union(_types,[type])
+    _types = _.union(_types,[type]);
 
     scope.$on(type,function(event,packet){
       var _id     = scope.$id;
       var _to     = packet.to;
       var _from   = packet.from;
-      var _type   = packet.type
-      var _time   = packet.time
-      var _group  = (!(_.isUndefined(scope.panel))) ? scope.panel.group : ["NONE"] 
+      var _type   = packet.type;
+      var _time   = packet.time;
+      var _group  = (!(_.isUndefined(scope.panel))) ? scope.panel.group : ["NONE"];
 
-      if(!(_.isArray(_to)))
+      if(!(_.isArray(_to))) {
         _to = [_to];
-      if(!(_.isArray(_group)))
+      }
+      if(!(_.isArray(_group))) {
         _group = [_group];
-      
+      }
+
       // Transmit event only if the sender is not the receiver AND one of the following:
       // 1) Receiver has group in _to 2) Receiver's $id is in _to
       // 3) Event is addressed to ALL 4) Receiver is in ALL group 
@@ -62,7 +67,7 @@ angular.module('kibana.services', [])
         fn(event,packet.data,{time:_time,to:_to,from:_from,type:_type});
       }
     });
-  }
+  };
 })
 /* 
   Service: fields
@@ -71,11 +76,11 @@ angular.module('kibana.services', [])
 .factory('fields', function($rootScope) {
   var fields = {
     list : []
-  }
+  };
 
   $rootScope.$on('fields', function(event,f) {
-    fields.list = _.union(f.data.all,fields.list)
-  })
+    fields.list = _.union(f.data.all,fields.list);
+  });
 
   return fields;
 
@@ -93,8 +98,8 @@ angular.module('kibana.services', [])
     return all_indices().then(function(p) {
       var indices = _.intersection(possible,p);
       indices.reverse();
-      return indices
-    })
+      return indices;
+    });
   };
 
   // returns a promise containing an array of all indices in an elasticsearch
@@ -110,7 +115,11 @@ angular.module('kibana.services', [])
     return something.then(function(p) {
       var indices = [];
       _.each(p.data, function(v,k) {
-        indices.push(k)
+        indices.push(k);
+        // Also add the aliases. Could be expensive on systems with a lot of them
+        _.each(v.aliases, function(v, k) {
+          indices.push(k);
+        });
       });
       return indices;
     });
@@ -122,7 +131,7 @@ angular.module('kibana.services', [])
   // Update: I just read this again. I died a little more inside.
   // Update2: More death.
   function fake_utc(date) {
-    date = moment(date).clone().toDate()
+    date = moment(date).clone().toDate();
     return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000));
   }
 
@@ -136,20 +145,20 @@ angular.module('kibana.services', [])
         range.push(start.clone());
         switch (interval) {
         case 'hour':
-          start.add('hours',1)
-          break
+          start.add('hours',1);
+          break;
         case 'day':
-          start.add('days',1)
-          break
+          start.add('days',1);
+          break;
         case 'week':
-          start.add('weeks',1)
-          break
+          start.add('weeks',1);
+          break;
         case 'month':
-          start.add('months',1)
-          break
+          start.add('months',1);
+          break;
         case 'year':
-          start.add('years',1)
-          break
+          start.add('years',1);
+          break;
         }
       }
       range.push(moment(end).clone());
@@ -169,19 +178,19 @@ angular.module('kibana.services', [])
   this.register = function(promise) {
     timers.push(promise);
     return promise;
-  }
+  };
 
   this.cancel = function(promise) {
-    timers = _.without(timers,promise)
-    $timeout.cancel(promise)
-  }
+    timers = _.without(timers,promise);
+    $timeout.cancel(promise);
+  };
 
   this.cancel_all = function() {
     _.each(timers, function(t){
       $timeout.cancel(t);
     });
-    timers = new Array();
-  }
+    timers = [];
+  };
 
 })
 .service('query', function(dashboard) {
@@ -214,10 +223,10 @@ angular.module('kibana.services', [])
     self.list = dashboard.current.services.query.list;
     self.ids = dashboard.current.services.query.ids;
     
-    if (self.ids.length == 0) {
+    if (self.ids.length === 0) {
       self.set({});
     }
-  }
+  };
 
   // This is used both for adding queries and modifying them. If an id is passed, the query at that id is updated
   this.set = function(query,id) {
@@ -235,42 +244,44 @@ angular.module('kibana.services', [])
         alias: '',
         color: colorAt(_id),
         id: _id
-      }
-      _.defaults(query,_query)
+      };
+      _.defaults(query,_query);
       self.list[_id] = query;
-      self.ids.push(_id)
+      self.ids.push(_id);
       return _id;
     }
-  }
+  };
 
   this.remove = function(id) {
     if(!_.isUndefined(self.list[id])) {
       delete self.list[id];
       // This must happen on the full path also since _.without returns a copy
-      self.ids = dashboard.current.services.query.ids = _.without(self.ids,id)
-      _q.idQueue.unshift(id)
-      _q.idQueue.sort(function(a,b){return a-b});
+      self.ids = dashboard.current.services.query.ids = _.without(self.ids,id);
+      _q.idQueue.unshift(id);
+      _q.idQueue.sort(function(v,k){
+        return v-k;
+      });
       return true;
     } else {
       return false;
     }
-  }
+  };
 
   this.findQuery = function(queryString) {
-    return _.findWhere(self.list,{query:queryString})
-  }
+    return _.findWhere(self.list,{query:queryString});
+  };
 
   var nextId = function() {
     if(_q.idQueue.length > 0) {
-      return _q.idQueue.shift()
+      return _q.idQueue.shift();
     } else {
       return self.ids.length;
     }
-  }
+  };
 
   var colorAt = function(id) {
-    return self.colors[id % self.colors.length]
-  }
+    return self.colors[id % self.colors.length];
+  };
 
   self.init();
 
@@ -281,7 +292,7 @@ angular.module('kibana.services', [])
   _.defaults(dashboard.current.services.filter,{
     idQueue : [],
     list : {},
-    ids : [],
+    ids : []
   });
 
   // For convenience
@@ -299,16 +310,16 @@ angular.module('kibana.services', [])
     _f = dashboard.current.services.filter;
 
     _.each(self.getByType('time',true),function(time) {
-      self.list[time.id].from = new Date(time.from)
-      self.list[time.id].to = new Date(time.to)
-    })
+      self.list[time.id].from = new Date(time.from);
+      self.list[time.id].to = new Date(time.to);
+    });
 
-  }
+  };
 
   // This is used both for adding filters and modifying them. 
   // If an id is passed, the filter at that id is updated
   this.set = function(filter,id) {
-    _.defaults(filter,{mandate:'must'})
+    _.defaults(filter,{mandate:'must'});
     filter.active = true;
     if(!_.isUndefined(id)) {
       if(!_.isUndefined(self.list[id])) {
@@ -325,18 +336,19 @@ angular.module('kibana.services', [])
         var _filter = {
           alias: '',
           id: _id
-        }
-        _.defaults(filter,_filter)
+        };
+        _.defaults(filter,_filter);
         self.list[_id] = filter;
-        self.ids.push(_id)
+        self.ids.push(_id);
         return _id;
       }
     }
-  }
+  };
 
   this.getBoolFilter = function(ids) {
     // A default match all filter, just in case there are no other filters
     var bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
+    var either_bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
     _.each(ids,function(id) {
       if(self.list[id].active) {
         switch(self.list[id].mandate) 
@@ -344,75 +356,69 @@ angular.module('kibana.services', [])
         case 'mustNot':
           bool = bool.mustNot(self.getEjsObj(id));
           break;
-        case 'should':
-          bool = bool.should(self.getEjsObj(id));
+        case 'either':
+          either_bool = either_bool.should(self.getEjsObj(id));
           break;
         default:
           bool = bool.must(self.getEjsObj(id));
         }
       }
-    })
-    return bool;
-  }
+    });
+    return bool.must(either_bool);
+  };
 
   this.getEjsObj = function(id) {
-    return self.toEjsObj(self.list[id])
-  }
+    return self.toEjsObj(self.list[id]);
+  };
 
   this.toEjsObj = function (filter) {
     if(!filter.active) {
-      return false
+      return false;
     }
     switch(filter.type)
     {
     case 'time':
       return ejs.RangeFilter(filter.field)
         .from(filter.from)
-        .to(filter.to)
-      break;
+        .to(filter.to);
     case 'range':
       return ejs.RangeFilter(filter.field)
         .from(filter.from)
-        .to(filter.to)
-      break;
+        .to(filter.to);
     case 'querystring':
-      return ejs.QueryFilter(ejs.QueryStringQuery(filter.query))
-      break;
+      return ejs.QueryFilter(ejs.QueryStringQuery(filter.query));
     case 'terms':
-      return ejs.TermsFilter(filter.field,filter.value)
-      break;
+      return ejs.TermsFilter(filter.field,filter.value);
     case 'exists':
-      return ejs.ExistsFilter(filter.field)
-      break;
+      return ejs.ExistsFilter(filter.field);
     case 'missing':
-      return ejs.MissingFilter(filter.field)
-      break;
+      return ejs.MissingFilter(filter.field);
     default:
       return false;
     }
-  }
+  };
 
   this.getByType = function(type,inactive) {
-    return _.pick(self.list,self.idsByType(type,inactive))
-  }
+    return _.pick(self.list,self.idsByType(type,inactive));
+  };
 
   this.removeByType = function(type) {
-    var ids = self.idsByType(type)
+    var ids = self.idsByType(type);
     _.each(ids,function(id) {
-      self.remove(id)
-    })
+      self.remove(id);
+    });
     return ids;
-  }
+  };
 
   this.idsByType = function(type,inactive) {
-    var _require = inactive ? {type:type} : {type:type,active:true}
-    return _.pluck(_.where(self.list,_require),'id')
-  }
+    var _require = inactive ? {type:type} : {type:type,active:true};
+    return _.pluck(_.where(self.list,_require),'id');
+  };
 
   // This special function looks for all time filters, and returns a time range according to the mode
   this.timeRange = function(mode) {
-    var _t = _.where(self.list,{type:'time',active:true})
-    if(_t.length == 0) {
+    var _t = _.where(self.list,{type:'time',active:true});
+    if(_t.length === 0) {
       return false;
     }
     switch(mode) {
@@ -420,41 +426,38 @@ angular.module('kibana.services', [])
       return {
         from: new Date(_.max(_.pluck(_t,'from'))),
         to: new Date(_.min(_.pluck(_t,'to')))
-      }
-      break;
+      };
     case "max":
       return {
         from: new Date(_.min(_.pluck(_t,'from'))),
         to: new Date(_.max(_.pluck(_t,'to')))
-      }
-      break;
+      };
     default:
       return false;
     }
-
-  } 
+  };
 
   this.remove = function(id) {
     if(!_.isUndefined(self.list[id])) {
       delete self.list[id];
       // This must happen on the full path also since _.without returns a copy
-      self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id)
-      _f.idQueue.unshift(id)
-      _f.idQueue.sort(function(a,b){return a-b});
+      self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id);
+      _f.idQueue.unshift(id);
+      _f.idQueue.sort(function(v,k){return v-k;});
       return true;
     } else {
       return false;
     }
-  }
+  };
 
 
   var nextId = function() {
     if(_f.idQueue.length > 0) {
-      return _f.idQueue.shift()
+      return _f.idQueue.shift();
     } else {
       return self.ids.length;
     }
-  }
+  };
 
   // Now init
   self.init();
@@ -491,7 +494,7 @@ angular.module('kibana.services', [])
     self.current = {};
     self.indices = [];
     route();
-  })
+  });
 
   var route = function() {
     // Is there a dashboard type and id in the URL?
@@ -499,29 +502,36 @@ angular.module('kibana.services', [])
       var _type = $routeParams.type;
       var _id = $routeParams.id;
 
-      if(_type === 'elasticsearch')
-        self.elasticsearch_load('dashboard',_id)
-      if(_type === 'temp')
-        self.elasticsearch_load('temp',_id)
-      if(_type === 'file')
-        self.file_load(_id)
+      switch(_type) {
+      case ('elasticsearch'):
+        self.elasticsearch_load('dashboard',_id);
+        break;
+      case ('temp'):
+        self.elasticsearch_load('temp',_id);
+        break;
+      case ('file'):
+        self.file_load(_id);
+        break;
+      default:
+        self.file_load('default.json');
+      }
 
     // No dashboard in the URL
     } else {
       // Check if browser supports localstorage, and if there's a dashboard 
-      if (Modernizr.localstorage && 
-        !(_.isUndefined(localStorage['dashboard'])) &&
-        localStorage['dashboard'] !== ''
+      if (window.Modernizr.localstorage && 
+        !(_.isUndefined(window.localStorage['dashboard'])) &&
+        window.localStorage['dashboard'] !== ''
       ) {
-        var dashboard = JSON.parse(localStorage['dashboard']);
+        var dashboard = JSON.parse(window.localStorage['dashboard']);
         _.defaults(dashboard,_dash);
-        self.dash_load(dashboard)
+        self.dash_load(dashboard);
       // No? Ok, grab default.json, its all we have now
       } else {
-        self.file_load('default.json')
+        self.file_load('default.json');
       } 
     }
-  }
+  };
 
   // Since the dashboard is responsible for index computation, we can compute and assign the indices 
   // here before telling the panels to refresh
@@ -535,21 +545,21 @@ angular.module('kibana.services', [])
           if(p.length > 0) {
             self.indices = p;          
           } else {
-            self.indices = [self.current.index.default]
+            self.indices = [self.current.index.default];
           }
-          $rootScope.$broadcast('refresh')
+          $rootScope.$broadcast('refresh');
         });
       } else {
         // This is not optimal, we should be getting the entire index list here, or at least every
         // index that possibly matches the pattern
-        self.indices = [self.current.index.default]
-        $rootScope.$broadcast('refresh')
+        self.indices = [self.current.index.default];
+        $rootScope.$broadcast('refresh');
       }
     } else {
-      self.indices = [self.current.index.default]
-      $rootScope.$broadcast('refresh')
+      self.indices = [self.current.index.default];
+      $rootScope.$broadcast('refresh');
     }
-  }
+  };
 
   this.dash_load = function(dashboard) {
     // Cancel all timers
@@ -557,88 +567,89 @@ angular.module('kibana.services', [])
 
     // If not using time based indices, use the default index
     if(dashboard.index.interval === 'none') {
-      self.indices = [dashboard.index.default]
+      self.indices = [dashboard.index.default];
     }
 
     self.current = _.clone(dashboard);
 
     // Ok, now that we've setup the current dashboard, we can inject our services
     query = $injector.get('query');
-    filterSrv = $injector.get('filterSrv')
+    filterSrv = $injector.get('filterSrv');
 
     // Make sure these re-init
     query.init();
     filterSrv.init();
 
-    if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length == 0) {
-    //if(dashboard.index.interval !== 'none') {
+    if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length === 0) {
       self.refresh();
     }
 
     return true;
-  }
+  };
 
   this.gist_id = function(string) {
-    if(self.is_gist(string))
+    if(self.is_gist(string)) {
       return string.match(gist_pattern)[0].replace(/.*\//, '');
-  }
+    }
+  };
 
   this.is_gist = function(string) {
-    if(!_.isUndefined(string) && string != '' && !_.isNull(string.match(gist_pattern)))
+    if(!_.isUndefined(string) && string !== '' && !_.isNull(string.match(gist_pattern))) {
       return string.match(gist_pattern).length > 0 ? true : false;
-    else
-      return false
-  }
+    } else {
+      return false;
+    }
+  };
 
   this.to_file = function() {
     var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
     // from filesaver.js
-    saveAs(blob, self.current.title+"-"+new Date().getTime());
+    window.saveAs(blob, self.current.title+"-"+new Date().getTime());
     return true;
-  }
+  };
 
   this.set_default = function(dashboard) {
-    if (Modernizr.localstorage) {
-      localStorage['dashboard'] = angular.toJson(dashboard || self.current);
+    if (window.Modernizr.localstorage) {
+      window.localStorage['dashboard'] = angular.toJson(dashboard || self.current);
       return true;
     } else {
       return false;
     }  
-  }
+  };
 
   this.purge_default = function() {
-    if (Modernizr.localstorage) {
-      localStorage['dashboard'] = '';
+    if (window.Modernizr.localstorage) {
+      window.localStorage['dashboard'] = '';
       return true;
     } else {
       return false;
     }
-  }
+  };
 
   // TOFIX: Pretty sure this breaks when you're on a saved dashboard already
   this.share_link = function(title,type,id) {
     return {
-      location  : location.href.replace(location.hash,""),
+      location  : window.location.href.replace(window.location.hash,""),
       type      : type,
       id        : id,
-      link      : location.href.replace(location.hash,"")+"#dashboard/"+type+"/"+id,
+      link      : window.location.href.replace(window.location.hash,"")+"#dashboard/"+type+"/"+id,
       title     : title
     };
-  }
+  };
 
   this.file_load = function(file) {
     return $http({
       url: "dashboards/"+file,
       method: "GET",
     }).then(function(result) {
-      var _dashboard = result.data
+      var _dashboard = result.data;
       _.defaults(_dashboard,_dash);
       self.dash_load(_dashboard);
       return true;
     },function(result) {
       return false;
     });
-  }
+  };
 
   this.elasticsearch_load = function(type,id) {
     var request = ejs.Request().indices(config.kibana_index).types(type);
@@ -649,19 +660,20 @@ angular.module('kibana.services', [])
       if(_.isUndefined(results)) {
         return false;
       } else {
-        self.dash_load(angular.fromJson(results.hits.hits[0]['_source']['dashboard']))
+        self.dash_load(angular.fromJson(results.hits.hits[0]['_source']['dashboard']));
         return true;
       }
     });
-  }
+  };
 
   this.elasticsearch_save = function(type,title,ttl) {
     // Clone object so we can modify it without influencing the existing obejct
-    var save = _.clone(self.current)
-
+    var save = _.clone(self.current);
+    var id;
+    
     // Change title on object clone
     if (type === 'dashboard') {
-      var id = save.title = _.isUndefined(title) ? self.current.title : title;
+      id = save.title = _.isUndefined(title) ? self.current.title : title;
     }
 
     // Create request with id as title. Rethink this.
@@ -670,10 +682,10 @@ angular.module('kibana.services', [])
       group: 'guest',
       title: save.title,
       dashboard: angular.toJson(save)
-    })
+    });
     
-    if (type === 'temp')
-      request = request.ttl(ttl)
+    request = type === 'temp' ? request.ttl(ttl) : request;
+
 
     // TOFIX: Implement error handling here
     return request.doIndex(
@@ -686,7 +698,7 @@ angular.module('kibana.services', [])
         return false;
       }
     );
-  }
+  };
 
   this.elasticsearch_delete = function(id) {
     return ejs.Document(config.kibana_index,'dashboard',id).doDelete(
@@ -699,7 +711,7 @@ angular.module('kibana.services', [])
         return false;
       }
     );
-  }
+  };
 
   this.elasticsearch_list = function(query,count) {
     var request = ejs.Request().indices(config.kibana_index).types('dashboard');
@@ -715,11 +727,11 @@ angular.module('kibana.services', [])
           return false;
         }
       );
-  }
+  };
 
   // TOFIX: Gist functionality
   this.save_gist = function(title,dashboard) {
-    var save = _.clone(dashboard || self.current)
+    var save = _.clone(dashboard || self.current);
     save.title = title || self.current.title;
     return $http({
       url: "https://api.github.com/gists",
@@ -738,16 +750,16 @@ angular.module('kibana.services', [])
     }, function(data, status, headers, config) {
       return false;
     });
-  }
+  };
 
   this.gist_list = function(id) {
     return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
     ).then(function(response) {
-      var files = []
+      var files = [];
       _.each(response.data.data.files,function(v,k) {
         try {
-          var file = JSON.parse(v.content)
-          files.push(file)
+          var file = JSON.parse(v.content);
+          files.push(file);
         } catch(e) {
           // Nothing?
         }
@@ -756,20 +768,6 @@ angular.module('kibana.services', [])
     }, function(data, status, headers, config) {
       return false;
     });
-  }
-
-})
-.service('keylistener', function($rootScope) {
-  var keys = [];
-  $(document).keydown(function (e) {
-    keys[e.which] = true;
-  });
-
-  $(document).keyup(function (e) {
-    delete keys[e.which];
-  });
+  };
 
-  this.keyActive = function(key) {
-    return keys[key] == true;
-  }
-});
+});

+ 48 - 41
panels/bettermap/module.js

@@ -1,3 +1,6 @@
+/*jshint globalstrict:true */
+/*global angular:true */
+/*global L:false*/
 /*
 
   ## Better maps
@@ -14,6 +17,8 @@
   * spyable :: Show the 'eye' icon that reveals the last ES query
 */
 
+'use strict';
+
 angular.module('kibana.bettermap', [])
 .controller('bettermap', function($scope, query, dashboard, filterSrv) {
 
@@ -26,61 +31,61 @@ angular.module('kibana.bettermap', [])
     tooltip : "_id",
     field   : null,
     group   : "default"
-  }
-  _.defaults($scope.panel,_d)
+  };
+  _.defaults($scope.panel,_d);
 
   $scope.init = function() {
     $scope.$on('refresh',function(){
       $scope.get_data();
-    })
+    });
     $scope.get_data();
-  }
+  };
 
   $scope.get_data = function(segment,query_id) {
     $scope.panel.error =  false;
 
     // Make sure we have everything for the request to complete
-    if(dashboard.indices.length == 0) {
+    if(dashboard.indices.length === 0) {
       return;
     }
 
     if(_.isUndefined($scope.panel.field)) {
-      $scope.panel.error = "Please select a field that contains geo point in [lon,lat] format"
-      return
+      $scope.panel.error = "Please select a field that contains geo point in [lon,lat] format";
+      return;
     }
     
     // Determine the field to sort on
-    var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'))
+    var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'));
     if(timeField.length > 1) {
-      $scope.panel.error = "Time field must be consistent amongst time filters"
-    } else if(timeField.length == 0) {
+      $scope.panel.error = "Time field must be consistent amongst time filters";
+    } else if(timeField.length === 0) {
       timeField = null;
     } else {
-      timeField = timeField[0]
+      timeField = timeField[0];
     }
 
-    var _segment = _.isUndefined(segment) ? 0 : segment
+    var _segment = _.isUndefined(segment) ? 0 : segment;
 
-    var boolQuery = ejs.BoolQuery();
+    var boolQuery = $scope.ejs.BoolQuery();
     _.each(query.list,function(q) {
-      boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*')))
-    })
+      boolQuery = boolQuery.should($scope.ejs.QueryStringQuery((q.query || '*')));
+    });
 
     var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
-      .query(ejs.FilteredQuery(
+      .query($scope.ejs.FilteredQuery(
         boolQuery,
-        filterSrv.getBoolFilter(filterSrv.ids).must(ejs.ExistsFilter($scope.panel.field))
+        filterSrv.getBoolFilter(filterSrv.ids).must($scope.ejs.ExistsFilter($scope.panel.field))
       ))
       .fields([$scope.panel.field,$scope.panel.tooltip])
-      .size($scope.panel.size)
+      .size($scope.panel.size);
 
     if(!_.isNull(timeField)) {
       request = request.sort(timeField,'desc');
     }
 
-    $scope.populate_modal(request)
+    $scope.populate_modal(request);
 
-    var results = request.doSearch()
+    var results = request.doSearch();
 
     // Populate scope when we have results
     results.then(function(results) {
@@ -89,7 +94,7 @@ angular.module('kibana.bettermap', [])
       if(_segment === 0) {
         $scope.hits = 0;
         $scope.data = [];
-        query_id = $scope.query_id = new Date().getTime()
+        query_id = $scope.query_id = new Date().getTime();
       }
 
       // Check for error and abort if found
@@ -101,31 +106,32 @@ angular.module('kibana.bettermap', [])
       // Check that we're still on the same query, if not stop
       if($scope.query_id === query_id) {
 
-        var scripts = $LAB.script("panels/bettermap/lib/leaflet.js").wait()
+        var scripts = $LAB.script("panels/bettermap/lib/leaflet.js").wait();
 
         scripts.wait(function(){
           $scope.data = $scope.data.concat(_.map(results.hits.hits, function(hit) {
             return {
               coordinates : new L.LatLng(hit.fields[$scope.panel.field][1],hit.fields[$scope.panel.field][0]),
               tooltip : hit.fields[$scope.panel.tooltip]
-            }
+            };
           }));
         });
         // Keep only what we need for the set
-        $scope.data = $scope.data.slice(0,$scope.panel.size)
+        $scope.data = $scope.data.slice(0,$scope.panel.size);
 
       } else {
         return;
       }
   
-      $scope.$emit('draw')
+      $scope.$emit('draw');
 
       // Get $size results then stop querying
-      if($scope.data.length < $scope.panel.size && _segment+1 < dashboard.indices.length)
-        $scope.get_data(_segment+1,$scope.query_id)
+      if($scope.data.length < $scope.panel.size && _segment+1 < dashboard.indices.length) {
+        $scope.get_data(_segment+1,$scope.query_id);
+      }
 
     });
-  }
+  };
 
   // I really don't like this function, too much dom manip. Break out into directive?
   $scope.populate_modal = function(request) {
@@ -135,8 +141,8 @@ angular.module('kibana.bettermap', [])
           'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
           angular.toJson(JSON.parse(request.toString()),true)+
         "'</pre>", 
-    } 
-  }
+    }; 
+  };
 
 })
 .directive('bettermap', function() {
@@ -144,7 +150,7 @@ angular.module('kibana.bettermap', [])
     restrict: 'A',
     link: function(scope, elem, attrs) {
 
-      elem.html('<center><img src="common/img/load_big.gif"></center>')
+      elem.html('<center><img src="common/img/load_big.gif"></center>');
 
       // Receive render events
       scope.$on('draw',function(){
@@ -154,9 +160,9 @@ angular.module('kibana.bettermap', [])
       scope.$on('render', function(){
         if(!_.isUndefined(map)) {
           map.invalidateSize();
-          var panes = map.getPanes()
+          var panes = map.getPanes();
         }
-      })
+      });
 
       var map, markers, layerGroup, mcg;
 
@@ -164,7 +170,7 @@ angular.module('kibana.bettermap', [])
         scope.panel.loading = false;
 
         var scripts = $LAB.script("panels/bettermap/lib/leaflet.js").wait()
-          .script("panels/bettermap/lib/plugins.js")
+          .script("panels/bettermap/lib/plugins.js");
    
         //add markers dynamically
         scripts.wait(function(){
@@ -185,16 +191,17 @@ angular.module('kibana.bettermap', [])
           }
 
           _.each(scope.data, function(p) {
-            if(!_.isUndefined(p.tooltip) && p.tooltip !== '')
-              layerGroup.addLayer(L.marker(p.coordinates).bindLabel(p.tooltip))
-            else
-              layerGroup.addLayer(L.marker(p.coordinates))
-          })
+            if(!_.isUndefined(p.tooltip) && p.tooltip !== '') {
+              layerGroup.addLayer(L.marker(p.coordinates).bindLabel(p.tooltip));
+            } else {
+              layerGroup.addLayer(L.marker(p.coordinates));
+            }
+          });
 
-          layerGroup.addTo(map)
+          layerGroup.addTo(map);
 
           map.fitBounds(_.pluck(scope.data,'coordinates'));
-        })
+        });
       }
     }
   };

+ 19 - 20
panels/column/module.js

@@ -1,3 +1,5 @@
+/*jshint globalstrict:true */
+/*global angular:true */
 /*
 
   ## Column
@@ -8,26 +10,23 @@
 
   ### Parameters
   * panels :: an array of panel objects. All of their spans should be set to 12
-
-  ### Group Events
-  #### Sends
-  * time :: Object Includes from, to and index
-
+  
 */
 
+'use strict';
+
 angular.module('kibana.column', [])
-.controller('column', function($scope, $rootScope) {
+.controller('column', function($scope, $rootScope, $timeout) {
   // Set and populate defaults
   var _d = {
     status: "Stable",
-    panels : [
-      ]
-  }
+    panels : []
+  };
   _.defaults($scope.panel,_d);
 
   $scope.init = function(){
     $scope.reset_panel();
-  }
+  };
 
   $scope.toggle_row = function(panel) {
     panel.collapse = panel.collapse ? false : true;
@@ -36,15 +35,15 @@ angular.module('kibana.column', [])
         $scope.send_render();
       });
     }
-  }
+  };
 
   $scope.send_render = function() {
     $scope.$broadcast('render');
-  }
+  };
 
   $scope.add_panel = function(panel) {
     $scope.panel.panels.push(panel);
-  }
+  };
 
   $scope.reset_panel = function(type) {
     $scope.new_panel = {
@@ -76,17 +75,17 @@ angular.module('kibana.column', [])
         $timeout(function() {
           // Create a reference to the new_panel as panel so that the existing
           // editors work with our isolate scope
-          scope.panel = scope.new_panel
-          var template = '<div ng-include src="\'panels/column/panelgeneral.html\'"></div>'
+          scope.panel = scope.new_panel;
+          var template = '<div ng-include src="\'panels/column/panelgeneral.html\'"></div>';
 
-          if(!(_.isUndefined(scope.type)) && scope.type != "")
+          if(!(_.isUndefined(scope.type)) && scope.type !== "") {
             template = template+'<div ng-include src="\'panels/'+scope.type+'/editor.html\'"></div>';
-          //var new_elem = $compile(angular.element(template))(scope))
+          }
           elem.html($compile(angular.element(template))(scope));
-        })
-      })   
+        });
+      });   
     }
-  }
+  };
 }).filter('withoutColumn', function() {
   return function() {
     return _.without(config.modules,'column');

+ 52 - 50
panels/dashcontrol/module.js

@@ -1,3 +1,6 @@
+/*jshint globalstrict:true */
+/*global angular:true */
+/*global FileReader:false*/
 /*
 
   ## Dashcontrol
@@ -20,11 +23,8 @@
   * temp :: Allow saving of temp dashboards
   * temp_ttl :: How long should temp dashboards persist
 
-  ### Group Events
-  #### Sends
-  * dashboard :: An object containing an entire dashboard to be loaded
-
 */
+'use strict';
 
 angular.module('kibana.dashcontrol', [])
 .controller('dashcontrol', function($scope, $http, timer, dashboard) {
@@ -49,7 +49,7 @@ angular.module('kibana.dashcontrol', [])
     elasticsearch_size: 20,
     temp: true,
     temp_ttl: '30d'
-  }
+  };
   _.defaults($scope.panel,_d);
 
   // A hash of defaults for the dashboard object
@@ -58,63 +58,63 @@ angular.module('kibana.dashcontrol', [])
     editable: true,
     rows: [],
     services: {}
-  }
+  };
 
   $scope.init = function() {
     $scope.gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
     $scope.gist = {};
     $scope.elasticsearch = {};
-  }
+  };
 
   $scope.set_default = function() {
     if(dashboard.set_default()) {
-      $scope.alert('Local Default Set',dashboard.current.title+' has been set as your local default','success',5000)
+      $scope.alert('Local Default Set',dashboard.current.title+' has been set as your local default','success',5000);
     } else {
-      $scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000)
+      $scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
     }
-  }
+  };
 
   $scope.purge_default = function() {
     if(dashboard.purge_default()) {
-      $scope.alert('Local Default Clear','Your local default dashboard has been cleared','success',5000)
+      $scope.alert('Local Default Clear','Your local default dashboard has been cleared','success',5000);
     } else {
-      $scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000)
+      $scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
     }
-  }
+  };
 
   $scope.elasticsearch_save = function(type,ttl) {
     dashboard.elasticsearch_save(type,($scope.elasticsearch.title || dashboard.current.title),ttl).then(
       function(result) {
       if(!_.isUndefined(result._id)) {
         $scope.alert('Dashboard Saved','This dashboard has been saved to Elasticsearch as "' + 
-          result._id + '"','success',5000)
+          result._id + '"','success',5000);
         if(type === 'temp') {
-          $scope.share = dashboard.share_link(dashboard.current.title,'temp',result._id)
+          $scope.share = dashboard.share_link(dashboard.current.title,'temp',result._id);
         }
       } else {
-        $scope.alert('Save failed','Dashboard could not be saved to Elasticsearch','error',5000)
+        $scope.alert('Save failed','Dashboard could not be saved to Elasticsearch','error',5000);
       }
-    })
-  }
+    });
+  };
 
   $scope.elasticsearch_delete = function(id) {
     dashboard.elasticsearch_delete(id).then(
       function(result) {
         if(!_.isUndefined(result)) {
           if(result.found) {
-            $scope.alert('Dashboard Deleted',id+' has been deleted','success',5000)
+            $scope.alert('Dashboard Deleted',id+' has been deleted','success',5000);
             // Find the deleted dashboard in the cached list and remove it
-            var toDelete = _.where($scope.elasticsearch.dashboards,{_id:id})[0]
-            $scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,toDelete)
+            var toDelete = _.where($scope.elasticsearch.dashboards,{_id:id})[0];
+            $scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,toDelete);
           } else {
-            $scope.alert('Dashboard Not Found','Could not find '+id+' in Elasticsearch','warning',5000)
+            $scope.alert('Dashboard Not Found','Could not find '+id+' in Elasticsearch','warning',5000);
           }
         } else {
-          $scope.alert('Dashboard Not Deleted','An error occurred deleting the dashboard',error,5000)
+          $scope.alert('Dashboard Not Deleted','An error occurred deleting the dashboard','error',5000);
         }
       }
-    )
-  }
+    );
+  };
 
   $scope.elasticsearch_dblist = function(query) {
     dashboard.elasticsearch_list(query,$scope.panel.elasticsearch_size).then(
@@ -122,10 +122,10 @@ angular.module('kibana.dashcontrol', [])
       if(!_.isUndefined(result.hits)) {
         $scope.panel.error =  false;
         $scope.hits = result.hits.total;
-        $scope.elasticsearch.dashboards = result.hits.hits
+        $scope.elasticsearch.dashboards = result.hits.hits;
       }
-    })
-  }
+    });
+  };
 
   $scope.save_gist = function() {
     dashboard.save_gist($scope.gist.title).then(
@@ -134,10 +134,10 @@ angular.module('kibana.dashcontrol', [])
         $scope.gist.last = link;
         $scope.alert('Gist saved','You will be able to access your exported dashboard file at <a href="'+link+'">'+link+'</a> in a moment','success');
       } else {
-        $scope.alert('Save failed','Gist could not be saved','error',5000)
+        $scope.alert('Save failed','Gist could not be saved','error',5000);
       }
-    })
-  }
+    });
+  };
 
   $scope.gist_dblist = function(id) {
     dashboard.gist_list(id).then(
@@ -145,10 +145,10 @@ angular.module('kibana.dashcontrol', [])
       if(files && files.length > 0) {
         $scope.gist.files = files;
       } else {
-        $scope.alert('Gist Failed','Could not retrieve dashboard list from gist','error',5000)
+        $scope.alert('Gist Failed','Could not retrieve dashboard list from gist','error',5000);
       }
-    })
-  }
+    });
+  };
 })
 .directive('dashUpload', function(timer, dashboard){
   return {
@@ -159,14 +159,15 @@ angular.module('kibana.dashcontrol', [])
 
         // files is a FileList of File objects. List some properties.
         var output = [];
+        var readerOnload = function(theFile) {
+          return function(e) {
+            dashboard.dash_load(JSON.parse(e.target.result));
+            scope.$apply();
+          };
+        };
         for (var i = 0, f; f = files[i]; i++) {
           var reader = new FileReader();
-          reader.onload = (function(theFile) {
-            return function(e) {
-              dashboard.dash_load(JSON.parse(e.target.result))
-              scope.$apply();
-            };
-          })(f);
+          reader.onload = (readerOnload)(f);
           reader.readAsText(f);
         }
       }
@@ -179,15 +180,16 @@ angular.module('kibana.dashcontrol', [])
         alert('Sorry, the HTML5 File APIs are not fully supported in this browser.');
       }
     }
-  }
+  };
 }).filter('gistid', function() {
-    var gist_pattern = /(\d{5,})|([a-z0-9]{10,})|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
-    return function(input, scope) {
-        //return input+"boners"
-        if(!(_.isUndefined(input))) {
-          var output = input.match(gist_pattern);
-          if(!_.isNull(output) && !_.isUndefined(output))
-            return output[0].replace(/.*\//, '');
-        }
+  var gist_pattern = /(\d{5,})|([a-z0-9]{10,})|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
+  return function(input, scope) {
+    //return input+"boners"
+    if(!(_.isUndefined(input))) {
+      var output = input.match(gist_pattern);
+      if(!_.isNull(output) && !_.isUndefined(output)) {
+        return output[0].replace(/.*\//, '');
+      }
     }
-});;
+  };
+});

+ 6 - 2
panels/fields/module.js

@@ -80,8 +80,12 @@ angular.module('kibana.fields', [])
   }
 
   $scope.build_search = function(field,value,mandate) {
-    var query = field+":"+angular.toJson(value)
-    
+    var query;
+    if(_.isArray(value)) {
+      query = field+":(" + _.map(value,function(v){return "\""+v+"\""}).join(",") + ")";
+    } else {
+      query = field+":"+angular.toJson(value);
+    }    
     filterSrv.set({type:'querystring',query:query,mandate:mandate})
     dashboard.refresh();
   }

+ 13 - 2
panels/filtering/module.html

@@ -18,7 +18,7 @@
     .filter-mustNot {
       border-bottom: #E24D42 3px solid;
     }
-    .filter-should {
+    .filter-either {
       border-bottom: #EF843C 3px solid;
     }
     .filter-action {
@@ -26,13 +26,24 @@
       margin-bottom: 0px !important;
       margin-left: 3px;
     }
+    .filter-mandate {
+      text-decoration: underline;
+      cursor: pointer;
+    }
 
   </style>
 
   <div class='filtering-container'>
     <div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter">
       <div class="filter-{{filterSrv.list[id].mandate}}">
-        <strong>{{filterSrv.list[id].type}}</strong> {{filterSrv.list[id].mandate}}
+        <strong>{{filterSrv.list[id].type}}</strong> 
+        <span ng-show="!filterSrv.list[id].editing" class="filter-mandate" ng-click="filterSrv.list[id].editing = true">{{filterSrv.list[id].mandate}}</span>
+
+        <span class="small" ng-show="filterSrv.list[id].editing">
+          <select class="input-small" ng-model="filterSrv.list[id].mandate" ng-options="f for f in ['must','mustNot','either']" ng-change='filterSrv.list[id].editing=undefined;refresh()'></select>
+          <i class="pointer icon-remove" bs-tooltip="'Cancel  '" ng-click="filterSrv.list[id].editing=undefined"></i>
+        </span>
+
         <i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i>
         <i class="filter-action pointer" ng-class="{'icon-check': filterSrv.list[id].active,'icon-check-empty': !filterSrv.list[id].active}" bs-tooltip="'Toggle'" ng-click="toggle(id)"></i>
 

+ 1 - 1
panels/filtering/module.js

@@ -40,7 +40,7 @@ angular.module('kibana.filtering', [])
   }
 
   $scope.show_key = function(key) {
-    return !_.contains(['type','id','alias','mandate','active'],key)
+    return !_.contains(['type','id','alias','mandate','active','editing'],key)
   }
 
 });

+ 8 - 2
panels/table/module.js

@@ -107,7 +107,13 @@ angular.module('kibana.table', [])
   }
 
   $scope.build_search = function(field,value,negate) {
-    var query = field+":"+angular.toJson(value)
+    var query;
+    // This needs to be abstracted somewhere
+    if(_.isArray(value)) {
+      query = field+":(" + _.map(value,function(v){return "\""+v+"\""}).join(",") + ")";
+    } else {
+      query = field+":"+angular.toJson(value);
+    }
     filterSrv.set({type:'querystring',query:query,mandate:(negate ? 'mustNot':'must')})
     $scope.panel.offset = 0;
     dashboard.refresh();
@@ -216,7 +222,7 @@ angular.module('kibana.table', [])
     $scope.modal = {
       title: "Table Inspector",
       body : "<h5>Last Elasticsearch Query</h5><pre>"+
-          'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
+          'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
           angular.toJson(JSON.parse(request.toString()),true)+
         "'</pre>", 
     } 

+ 4 - 4
panels/trends/module.js

@@ -1,12 +1,12 @@
+/*jshint globalstrict:true */
+/*global angular:true */
 /*
 
-  ## Hits
+  ## Trends
 
-  A variety of representations of the hits a query matches
+  Shows how queries are moving from a specified time ago
 
   ### Parameters
-  * query ::  An array of queries. No labels here, just an array of strings. Maybe
-              there should be labels. Probably. 
   * style :: A hash of css styles
   * arrangement :: How should I arrange the query results? 'horizontal' or 'vertical'
   * ago :: Date math formatted time to look back