You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by om...@apache.org on 2015/12/08 07:37:43 UTC
[19/51] [partial] incubator-metron git commit: Initial import of code
from https://github.com/OpenSOC/opensoc at
ac0b00373f8f56dfae03a8109af5feb373ea598e.
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/table/module.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/table/module.js b/opensoc-ui/lib/public/app/panels/table/module.js
new file mode 100755
index 0000000..c3fef73
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/table/module.js
@@ -0,0 +1,516 @@
+/** @scratch /panels/5
+ *
+ * include::panels/table.asciidoc[]
+ */
+
+/** @scratch /panels/table/0
+ *
+ * == table
+ * Status: *Stable*
+ *
+ * The table panel contains a sortable, pagable view of documents that. It can be arranged into
+ * defined columns and offers several interactions, such as performing adhoc terms aggregations.
+ *
+ */
+define([
+ 'angular',
+ 'app',
+ 'lodash',
+ 'kbn',
+ 'moment',
+],
+function (angular, app, _, kbn, moment) {
+ 'use strict';
+
+ var module = angular.module('kibana.panels.table', []);
+ app.useModule(module);
+
+ module.controller('table', function($rootScope, $scope, $modal, $q, $compile, $timeout,
+ fields, querySrv, dashboard, filterSrv) {
+ $scope.panelMeta = {
+ modals : [
+ {
+ description: "Inspect",
+ icon: "icon-info-sign",
+ partial: "app/partials/inspector.html",
+ show: $scope.panel.spyable
+ }
+ ],
+ editorTabs : [
+ {
+ title:'Paging',
+ src: 'app/panels/table/pagination.html'
+ },
+ {
+ title:'Queries',
+ src: 'app/partials/querySelect.html'
+ }
+ ],
+ status: "Stable",
+ description: "A paginated table of records matching your query or queries. Click on a row to "+
+ "expand it and review all of the fields associated with that document. <p>"
+ };
+
+ // Set and populate defaults
+ var _d = {
+ /** @scratch /panels/table/5
+ * === Parameters
+ *
+ * size:: The number of hits to show per page
+ */
+ size : 100, // Per page
+ /** @scratch /panels/table/5
+ * pages:: The number of pages available
+ */
+ pages : 5, // Pages available
+ /** @scratch /panels/table/5
+ * offset:: The current page
+ */
+ offset : 0,
+ /** @scratch /panels/table/5
+ * sort:: An array describing the sort order of the table. For example [`@timestamp',`desc']
+ */
+ sort : ['_score','desc'],
+ /** @scratch /panels/table/5
+ * overflow:: The css overflow property. `min-height' (expand) or `auto' (scroll)
+ */
+ overflow: 'min-height',
+ /** @scratch /panels/table/5
+ * fields:: the fields used a columns of the table, in an array.
+ */
+ fields : [],
+ /** @scratch /panels/table/5
+ * highlight:: The fields on which to highlight, in an array
+ */
+ highlight : [],
+ /** @scratch /panels/table/5
+ * sortable:: Set sortable to false to disable sorting
+ */
+ sortable: true,
+ /** @scratch /panels/table/5
+ * header:: Set to false to hide the table column names
+ */
+ header : true,
+ /** @scratch /panels/table/5
+ * paging:: Set to false to hide the paging controls of the table
+ */
+ paging : true,
+ /** @scratch /panels/table/5
+ * field_list:: Set to false to hide the list of fields. The user will be able to expand it,
+ * but it will be hidden by default
+ */
+ field_list: true,
+ /** @scratch /panels/table/5
+ * all_fields:: Set to true to show all fields in the mapping, not just the current fields in
+ * the table.
+ */
+ all_fields: false,
+ /** @scratch /panels/table/5
+ * trimFactor:: The trim factor is the length at which to truncate fields takinging into
+ * consideration the number of columns in the table. For example, a trimFactor of 100, with 5
+ * columns in the table, would trim each column at 20 character. The entirety of the field is
+ * still available in the expanded view of the event.
+ */
+ trimFactor: 300,
+ /** @scratch /panels/table/5
+ * localTime:: Set to true to adjust the timeField to the browser's local time
+ */
+ localTime: false,
+ /** @scratch /panels/table/5
+ * timeField:: If localTime is set to true, this field will be adjusted to the browsers local time
+ */
+ timeField: '@timestamp',
+ /** @scratch /panels/table/5
+ * spyable:: Set to false to disable the inspect icon
+ */
+ spyable : true,
+ /** @scratch /panels/table/5
+ *
+ * ==== Queries
+ * queries object:: This object describes the queries to use on this panel.
+ * queries.mode::: Of the queries available, which to use. Options: +all, pinned, unpinned, selected+
+ * queries.ids::: In +selected+ mode, which query ids are selected.
+ */
+ queries : {
+ mode : 'all',
+ ids : []
+ },
+ /*
+ * locked:: whether to lock the query, preventing it from being affected by filters
+ */
+ locked: false,
+ style : {'font-size': '9pt'},
+ normTimes : true,
+ };
+ _.defaults($scope.panel,_d);
+
+ $scope.init = function () {
+ $scope.columns = {};
+ _.each($scope.panel.fields,function(field) {
+ $scope.columns[field] = true;
+ });
+
+ $scope.Math = Math;
+ $scope.identity = angular.identity;
+ $scope.$on('refresh',function(){$scope.get_data();});
+
+ $scope.fields = fields;
+ $scope.get_data();
+ };
+
+ // Create a percent function for the view
+ $scope.percent = kbn.to_percent;
+
+ $scope.closeFacet = function() {
+ if($scope.modalField) {
+ delete $scope.modalField;
+ }
+ };
+
+ $scope.termsModal = function(field,chart) {
+ $scope.closeFacet();
+ $timeout(function() {
+ $scope.modalField = field;
+ showModal(
+ '{"height":"200px","chart":"'+chart+'","field":"'+field+'"}','terms');
+ },0);
+ };
+
+ $scope.statsModal = function(field) {
+ $scope.modalField = field;
+ showModal(
+ '{"field":"'+field+'"}','statistics');
+ };
+
+ var showModal = function(panel,type) {
+ $scope.facetPanel = panel;
+ $scope.facetType = type;
+
+ // create a new modal. Can't reuse one modal unforunately as the directive will not
+ // re-render on show.
+ /*
+ $modal({
+ template: './app/panels/table/modal.html',
+ persist: false,
+ show: true,
+ scope: $scope.$new(),
+ keyboard: false
+ });
+ */
+
+ };
+
+
+
+ $scope.toggle_micropanel = function(field,groups) {
+ var docs = _.map($scope.data,function(_d){return _d.kibana._source;});
+ var topFieldValues = kbn.top_field_values(docs,field,10,groups);
+ $scope.micropanel = {
+ field: field,
+ grouped: groups,
+ values : topFieldValues.counts,
+ hasArrays : topFieldValues.hasArrays,
+ related : kbn.get_related_fields(docs,field),
+ limit: 10,
+ count: _.countBy(docs,function(doc){return _.contains(_.keys(doc),field);})['true']
+ };
+ };
+
+ $scope.micropanelColor = function(index) {
+ var _c = ['bar-success','bar-warning','bar-danger','bar-info','bar-primary'];
+ return index > _c.length ? '' : _c[index];
+ };
+
+ $scope.set_sort = function(field) {
+ if($scope.panel.sort[0] === field) {
+ $scope.panel.sort[1] = $scope.panel.sort[1] === 'asc' ? 'desc' : 'asc';
+ } else {
+ $scope.panel.sort[0] = field;
+ }
+ $scope.get_data();
+ };
+
+ $scope.toggle_field = function(field) {
+ if (_.indexOf($scope.panel.fields,field) > -1) {
+ $scope.panel.fields = _.without($scope.panel.fields,field);
+ delete $scope.columns[field];
+ } else {
+ $scope.panel.fields.push(field);
+ $scope.columns[field] = true;
+ }
+ };
+
+ $scope.toggle_highlight = function(field) {
+ if (_.indexOf($scope.panel.highlight,field) > -1) {
+ $scope.panel.highlight = _.without($scope.panel.highlight,field);
+ } else {
+ $scope.panel.highlight.push(field);
+ }
+ };
+
+ $scope.toggle_details = function(row) {
+ row.kibana.details = row.kibana.details ? false : true;
+ row.kibana.view = row.kibana.view || 'table';
+ //row.kibana.details = !row.kibana.details ? $scope.without_kibana(row) : false;
+ };
+
+ $scope.page = function(page) {
+ $scope.panel.offset = page*$scope.panel.size;
+ $scope.get_data();
+ };
+
+ $scope.build_search = function(field,value,negate) {
+ var query;
+ // This needs to be abstracted somewhere
+ if(_.isArray(value)) {
+ query = "(" + _.map(value,function(v){return angular.toJson(v);}).join(" AND ") + ")";
+ } else if (_.isUndefined(value)) {
+ query = '*';
+ negate = !negate;
+ } else {
+ query = angular.toJson(value);
+ }
+ $scope.panel.offset = 0;
+ filterSrv.set({type:'field',field:field,query:query,mandate:(negate ? 'mustNot':'must')});
+ };
+
+ $scope.fieldExists = function(field,mandate) {
+ filterSrv.set({type:'exists',field:field,mandate:mandate});
+ };
+
+ $scope.get_data = function(segment,query_id) {
+ var
+ _segment,
+ request,
+ boolQuery,
+ queries,
+ sort;
+
+ $scope.panel.error = false;
+
+ // Make sure we have everything for the request to complete
+ if(dashboard.indices.length === 0) {
+ return;
+ }
+
+ sort = [$scope.ejs.Sort($scope.panel.sort[0]).order($scope.panel.sort[1])];
+ if($scope.panel.localTime) {
+ sort.push($scope.ejs.Sort($scope.panel.timeField).order($scope.panel.sort[1]));
+ }
+
+
+ $scope.panelMeta.loading = true;
+
+ _segment = _.isUndefined(segment) ? 0 : segment;
+ $scope.segment = _segment;
+
+ request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
+
+ $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
+
+ queries = querySrv.getQueryObjs($scope.panel.queries.ids);
+ console.log(queries)
+
+ boolQuery = $scope.ejs.BoolQuery();
+ _.each(queries,function(q) {
+ boolQuery = boolQuery.should(querySrv.toEjsObj(q));
+ });
+
+ request = request.query(
+ $scope.ejs.FilteredQuery(
+ boolQuery,
+ $scope.panel.locked ? null : filterSrv.getBoolFilter(filterSrv.ids())
+ ))
+ .highlight(
+ $scope.ejs.Highlight($scope.panel.highlight)
+ .fragmentSize(2147483647) // Max size of a 32bit unsigned int
+ .preTags('@start-highlight@')
+ .postTags('@end-highlight@')
+ )
+ .size($scope.panel.size*$scope.panel.pages)
+ .sort(sort);
+
+ $scope.populate_modal(request);
+
+ // Populate scope when we have results
+ request.doSearch().then(function(results) {
+ $scope.panelMeta.loading = false;
+
+ if(_segment === 0) {
+ $scope.panel.offset = 0;
+ $scope.hits = 0;
+ $scope.data = [];
+ $scope.current_fields = [];
+ query_id = $scope.query_id = new Date().getTime();
+ }
+
+ // Check for error and abort if found
+ if(!(_.isUndefined(results.error))) {
+ $scope.panel.error = $scope.parse_error(results.error);
+ return;
+ }
+
+ // Check that we're still on the same query, if not stop
+ if($scope.query_id === query_id) {
+
+ // This is exceptionally expensive, especially on events with a large number of fields
+ $scope.data = $scope.data.concat(_.map(results.hits.hits, function(hit) {
+ var
+ _h = _.clone(hit),
+ _p = _.omit(hit,'_source','sort','_score');
+
+ // _source is kind of a lie here, never display it, only select values from it
+ _h.kibana = {
+ _source : _.extend(kbn.flatten_json(hit._source),_p),
+ highlight : kbn.flatten_json(hit.highlight||{})
+ };
+
+ // Kind of cheating with the _.map here, but this is faster than kbn.get_all_fields
+ $scope.current_fields = $scope.current_fields.concat(_.keys(_h.kibana._source));
+
+ return _h;
+ }));
+
+ $scope.current_fields = _.uniq($scope.current_fields);
+ $scope.hits += results.hits.total;
+
+ // Sort the data
+ $scope.data = _.sortBy($scope.data, function(v){
+ if(!_.isUndefined(v.sort)) {
+ return v.sort[0];
+ } else {
+ return v._score;
+ }
+ });
+
+ // Reverse if needed
+ if($scope.panel.sort[1] === 'desc') {
+ $scope.data.reverse();
+ }
+
+ // Keep only what we need for the set
+ $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages);
+
+ } else {
+ return;
+ }
+
+ // If we're not sorting in reverse chrono order, query every index for
+ // size*pages results
+ // Otherwise, only get size*pages results then stop querying
+ if (($scope.data.length < $scope.panel.size*$scope.panel.pages ||
+ !((_.contains(filterSrv.timeField(),$scope.panel.sort[0])) && $scope.panel.sort[1] === 'desc')) &&
+ _segment+1 < dashboard.indices.length) {
+ $scope.get_data(_segment+1,$scope.query_id);
+ }
+
+ });
+ };
+
+ $scope.populate_modal = function(request) {
+ $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
+ };
+
+ $scope.without_kibana = function (row) {
+ var _c = _.clone(row);
+ delete _c.kibana;
+ return _c;
+ };
+
+ $scope.set_refresh = function (state) {
+ $scope.refresh = state;
+ };
+
+ $scope.close_edit = function() {
+ if($scope.refresh) {
+ $scope.get_data();
+ }
+ $scope.columns = [];
+ _.each($scope.panel.fields,function(field) {
+ $scope.columns[field] = true;
+ });
+ $scope.refresh = false;
+ };
+
+ $scope.locate = function(obj, path) {
+ path = path.split('.');
+ var arrayPattern = /(.+)\[(\d+)\]/;
+ for (var i = 0; i < path.length; i++) {
+ var match = arrayPattern.exec(path[i]);
+ if (match) {
+ obj = obj[match[1]][parseInt(match[2],10)];
+ } else {
+ obj = obj[path[i]];
+ }
+ }
+ return obj;
+ };
+
+
+ });
+
+ // This also escapes some xml sequences
+ module.filter('tableHighlight', function() {
+ return function(text) {
+ if (!_.isUndefined(text) && !_.isNull(text) && text.toString().length > 0) {
+ return text.toString().
+ replace(/&/g, '&').
+ replace(/</g, '<').
+ replace(/>/g, '>').
+ replace(/\r?\n/g, '<br/>').
+ replace(/@start-highlight@/g, '<code class="highlight">').
+ replace(/@end-highlight@/g, '</code>');
+ }
+ return '';
+ };
+ });
+
+ module.filter('tableTruncate', function() {
+ return function(text,length,factor) {
+ if (!_.isUndefined(text) && !_.isNull(text) && text.toString().length > 0) {
+ return text.length > length/factor ? text.substr(0,length/factor)+'...' : text;
+ }
+ return '';
+ };
+ });
+
+
+
+ module.filter('tableJson', function() {
+ var json;
+ return function(text,prettyLevel) {
+ if (!_.isUndefined(text) && !_.isNull(text) && text.toString().length > 0) {
+ json = angular.toJson(text,prettyLevel > 0 ? true : false);
+ json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+ if(prettyLevel > 1) {
+ /* jshint maxlen: false */
+ json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
+ var cls = 'number';
+ if (/^"/.test(match)) {
+ if (/:$/.test(match)) {
+ cls = 'key strong';
+ } else {
+ cls = '';
+ }
+ } else if (/true|false/.test(match)) {
+ cls = 'boolean';
+ } else if (/null/.test(match)) {
+ cls = 'null';
+ }
+ return '<span class="' + cls + '">' + match + '</span>';
+ });
+ }
+ return json;
+ }
+ return '';
+ };
+ });
+
+ // WIP
+ module.filter('tableLocalTime', function(){
+ return function(text,event) {
+ return moment(event.sort[1]).format("YYYY-MM-DDTHH:mm:ss.SSSZ");
+ };
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/table/pagination.html
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/table/pagination.html b/opensoc-ui/lib/public/app/panels/table/pagination.html
new file mode 100755
index 0000000..8eee4cc
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/table/pagination.html
@@ -0,0 +1,32 @@
+ <div class="editor-row">
+ <div class="section">
+ <div class="editor-option">
+ <h6>Show Controls</h6><input type="checkbox" ng-model="panel.paging" ng-checked="panel.paging">
+ </div>
+ <div class="editor-option">
+ <h6>Overflow</h6>
+ <select class="input-small" ng-model="panel.overflow" ng-options="f.value as f.key for f in [{key:'scroll',value:'height'},{key:'expand',value:'min-height'}]"></select>
+ </div>
+ </div>
+
+ <div class="section">
+ <div class="editor-option">
+ <h6>Per Page</h6>
+ <input type="number" class="input-mini" ng-model="panel.size" ng-change="get_data()">
+ </div>
+ <div class="editor-option">
+ <h6> </h6>
+ <center><i class='icon-remove'></i><center>
+ </div>
+ <div class="editor-option">
+ <h6>Page limit</h6>
+ <input type="number" class="input-mini" ng-model="panel.pages" ng-change="get_data()">
+ </div>
+ <div class="editor-option large">
+ <h6>Pageable</h6>
+ <strong class="large">= {{panel.size * panel.pages}}</strong>
+ </div>
+ </div>
+
+ </div>
+
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/terms/editor.html
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/terms/editor.html b/opensoc-ui/lib/public/app/panels/terms/editor.html
new file mode 100755
index 0000000..ee849a6
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/terms/editor.html
@@ -0,0 +1,70 @@
+ <div class="editor-row">
+ <div class="section">
+ <h5>Parameters</h5>
+ <div class="editor-option">
+ <label class="small">Terms mode</label>
+ <select class="input-medium" ng-model="panel.tmode" ng-options="f for f in ['terms','terms_stats']" ng-change="set_refresh(true)"></select>
+ </div>
+ <div class="editor-option" ng-show="panel.tmode == 'terms_stats'">
+ <label class="small">Stats type</label>
+ <select class="input-medium" ng-model="panel.tstat" ng-options="f for f in ['count', 'total_count', 'min', 'max', 'total', 'mean']"></select>
+ </div>
+ <div class="editor-option">
+ <label class="small">Field</label>
+ <input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.field" ng-change="set_refresh(true)">
+ </div>
+ <div class="editor-option" ng-show="panel.tmode == 'terms_stats'">
+ <label class="small">Value field</label>
+ <input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.valuefield" ng-change="set_refresh(true)">
+ </div>
+ <div class="editor-option">
+ <label class="small">Length</label>
+ <input class="input-small" type="number" ng-model="panel.size" ng-change="set_refresh(true)">
+ </div>
+ <div class="editor-option">
+ <label class="small">Order</label>
+ <select class="input-medium" ng-model="panel.order" ng-options="f for f in ['count','term','reverse_count','reverse_term']" ng-change="set_refresh(true)" ng-show="panel.tmode == 'terms'"></select>
+ <select class="input-medium" ng-model="panel.order" ng-options="f for f in ['term', 'reverse_term', 'count', 'reverse_count', 'total', 'reverse_total', 'min', 'reverse_min', 'max', 'reverse_max', 'mean', 'reverse_mean']" ng-change="set_refresh(true)" ng-show="panel.tmode == 'terms_stats'"></select>
+ </div>
+ <div class="editor-option" ng-show="panel.tmode == 'terms'">
+ <label class="small">Exclude Terms(s) (comma separated)</label>
+ <input array-join type="text" ng-model='panel.exclude'></input>
+ </div>
+ </div>
+ </div>
+ <div class="editor-row">
+ <div class="section">
+ <h5>View Options</h5>
+ <div class="editor-option">
+ <label class="small">Style</label>
+ <select class="input-small" ng-model="panel.chart" ng-options="f for f in ['bar','pie','table']"></select></span>
+ </div>
+ <div class="editor-option" ng-show="panel.chart == 'table'">
+ <label class="small">Font Size</label>
+ <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
+ </div>
+ <div class="editor-option" ng-show="panel.chart == 'bar' || panel.chart == 'pie'">
+ <label class="small">Legend</label>
+ <select class="input-small" ng-model="panel.counter_pos" ng-options="f for f in ['above','below','none']"></select></span>
+ </div>
+ <div class="editor-option" ng-show="panel.chart != 'table' && panel.counter_pos != 'none'">
+ <label class="small" >Legend Format</label>
+ <select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['horizontal','vertical']"></select></span>
+ </div>
+ <div class="editor-option">
+ <label class="small">Missing</label><input type="checkbox" ng-model="panel.missing" ng-checked="panel.missing">
+ </div>
+ <div class="editor-option">
+ <label class="small">Other</label><input type="checkbox" ng-model="panel.other" ng-checked="panel.other">
+ </div>
+ <div class="editor-option" ng-show="panel.chart == 'pie'">
+ <label class="small">Donut</label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
+ </div>
+ <div class="editor-option" ng-show="panel.chart == 'pie'">
+ <label class="small">Tilt</label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
+ </div>
+ <div class="editor-option" ng-show="panel.chart == 'pie'">
+ <label class="small">Labels</label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
+ </div>
+ </div>
+ </div>
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/terms/module.html
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/terms/module.html b/opensoc-ui/lib/public/app/panels/terms/module.html
new file mode 100755
index 0000000..2adaa3e
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/terms/module.html
@@ -0,0 +1,83 @@
+<div ng-controller='terms' ng-init="init()">
+ <style>
+ .pieLabel { pointer-events: none }
+ .terms-legend-term {
+ word-break: break-all;
+ }
+ </style>
+
+ <!-- START Pie or bar chart -->
+ <div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+ <!-- vertical legend above -->
+ <table class="small" ng-show="panel.arrangement == 'vertical'">
+ <tr ng-repeat="term in legend">
+ <td><i class="icon-circle" ng-style="{color:term.color}"></i></td>
+ <td class="terms-legend-term" style="padding-right:10px;padding-left:10px;">{{term.label}}</td>
+ <td>{{term.data[0][1]}}</td>
+ </tr>
+ </table>
+
+ <!-- horizontal legend above -->
+ <span class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="term in legend" style="float:left;padding-left: 10px;">
+ <span>
+ <i class="icon-circle" ng-style="{color:term.color}"></i>
+ <span class="terms-legend-term">{{term.label}}</span> ({{term.data[0][1]}})
+ </span>
+ </span>
+
+ <span class="small pull-left" ng-show="panel.tmode == 'terms_stats'">
+   | {{ panel.tstat }} of <strong>{{ panel.valuefield }}</strong>
+ </span>
+
+ </div>
+
+ <!-- keep legend from over lapping -->
+ <div style="clear:both"></div>
+
+ <div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" terms-chart params="{{panel}}" style="position:relative" class="pointer"></div>
+
+ <div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+ <!-- vertical legend below -->
+ <table class="small" ng-show="panel.arrangement == 'vertical'">
+ <tr ng-repeat="term in legend">
+ <td><i class="icon-circle" ng-style="{color:term.color}"></i></i></td>
+ <td class="terms-legend-term" style="padding-right:10px;padding-left:10px;">{{term.label}}</td>
+ <td>{{term.data[0][1]}}</td>
+ </tr>
+ </table>
+
+ <!-- horizontal legend below -->
+ <span class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="term in legend" style="float:left;padding-left: 10px;">
+ <span>
+ <i class="icon-circle" ng-style="{color:term.color}"></i>
+ <span class="terms-legend-term">{{term.label}}</span> ({{term.data[0][1]}})
+ </span>
+ </span>
+
+ <span class="small pull-left" ng-show="panel.tmode == 'terms_stats'">
+   | {{ panel.tstat }} of <strong>{{ panel.valuefield }}</strong>
+ </span>
+
+ <div style="clear:both"></div>
+ </div>
+ <!-- END Pie or Bar chart -->
+
+
+
+ <table ng-style="panel.style" class="table table-striped table-condensed" ng-show="panel.chart == 'table'">
+ <thead>
+ <th>Term</th> <th>{{ panel.tmode == 'terms_stats' ? panel.tstat : 'Count' }}</th> <th>Action</th>
+ </thead>
+ <tr ng-repeat="term in data" ng-show="showMeta(term)">
+ <td class="terms-legend-term">{{term.label}}</td>
+ <td>{{term.data[0][1]}}</td>
+ <td>
+ <span ng-hide="term.meta == 'other'">
+ <i class='icon-search pointer' ng-click="build_search(term)"></i>
+ <i class='icon-ban-circle pointer' ng-click="build_search(term,true)"></i>
+ </span>
+ </td>
+ </tr>
+ </table>
+
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/terms/module.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/terms/module.js b/opensoc-ui/lib/public/app/panels/terms/module.js
new file mode 100755
index 0000000..edb7735
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/terms/module.js
@@ -0,0 +1,413 @@
+/** @scratch /panels/5
+ *
+ * include::panels/terms.asciidoc[]
+ */
+
+/** @scratch /panels/terms/0
+ *
+ * == terms
+ * Status: *Stable*
+ *
+ * A table, bar chart or pie chart based on the results of an Elasticsearch terms facet.
+ *
+ */
+define([
+ 'angular',
+ 'app',
+ 'lodash',
+ 'jquery',
+ 'kbn'
+],
+function (angular, app, _, $, kbn) {
+ 'use strict';
+
+ var module = angular.module('kibana.panels.terms', []);
+ app.useModule(module);
+
+ module.controller('terms', function($scope, querySrv, dashboard, filterSrv, fields) {
+ $scope.panelMeta = {
+ modals : [
+ {
+ description: "Inspect",
+ icon: "icon-info-sign",
+ partial: "app/partials/inspector.html",
+ show: $scope.panel.spyable
+ }
+ ],
+ editorTabs : [
+ {title:'Queries', src:'app/partials/querySelect.html'}
+ ],
+ status : "Stable",
+ description : "Displays the results of an elasticsearch facet as a pie chart, bar chart, or a "+
+ "table"
+ };
+
+ // Set and populate defaults
+ var _d = {
+ /** @scratch /panels/terms/5
+ * === Parameters
+ *
+ * field:: The field on which to computer the facet
+ */
+ field : '_type',
+ /** @scratch /panels/terms/5
+ * exclude:: terms to exclude from the results
+ */
+ exclude : [],
+ /** @scratch /panels/terms/5
+ * missing:: Set to false to disable the display of a counter showing how much results are
+ * missing the field
+ */
+ missing : true,
+ /** @scratch /panels/terms/5
+ * other:: Set to false to disable the display of a counter representing the aggregate of all
+ * values outside of the scope of your +size+ property
+ */
+ other : true,
+ /** @scratch /panels/terms/5
+ * size:: Show this many terms
+ */
+ size : 10,
+ /** @scratch /panels/terms/5
+ * order:: In terms mode: count, term, reverse_count or reverse_term,
+ * in terms_stats mode: term, reverse_term, count, reverse_count,
+ * total, reverse_total, min, reverse_min, max, reverse_max, mean or reverse_mean
+ */
+ order : 'count',
+ style : { "font-size": '10pt'},
+ /** @scratch /panels/terms/5
+ * donut:: In pie chart mode, draw a hole in the middle of the pie to make a tasty donut.
+ */
+ donut : false,
+ /** @scratch /panels/terms/5
+ * tilt:: In pie chart mode, tilt the chart back to appear as more of an oval shape
+ */
+ tilt : false,
+ /** @scratch /panels/terms/5
+ * lables:: In pie chart mode, draw labels in the pie slices
+ */
+ labels : true,
+ /** @scratch /panels/terms/5
+ * arrangement:: In bar or pie mode, arrangement of the legend. horizontal or vertical
+ */
+ arrangement : 'horizontal',
+ /** @scratch /panels/terms/5
+ * chart:: table, bar or pie
+ */
+ chart : 'bar',
+ /** @scratch /panels/terms/5
+ * counter_pos:: The location of the legend in respect to the chart, above, below, or none.
+ */
+ counter_pos : 'above',
+ /** @scratch /panels/terms/5
+ * spyable:: Set spyable to false to disable the inspect button
+ */
+ spyable : true,
+ /** @scratch /panels/terms/5
+ *
+ * ==== Queries
+ * queries object:: This object describes the queries to use on this panel.
+ * queries.mode::: Of the queries available, which to use. Options: +all, pinned, unpinned, selected+
+ * queries.ids::: In +selected+ mode, which query ids are selected.
+ */
+ queries : {
+ mode : 'all',
+ ids : []
+ },
+
+ /*
+ * locked:: whether to lock the query, preventing it from being affected by filters
+ */
+ locked: false,
+ /** @scratch /panels/terms/5
+ * tmode:: Facet mode: terms or terms_stats
+ */
+ tmode : 'terms',
+ /** @scratch /panels/terms/5
+ * tstat:: Terms_stats facet stats field
+ */
+ tstat : 'total',
+ /** @scratch /panels/terms/5
+ * valuefield:: Terms_stats facet value field
+ */
+ valuefield : ''
+ };
+
+ _.defaults($scope.panel,_d);
+
+ $scope.init = function () {
+ $scope.hits = 0;
+
+ $scope.$on('refresh',function(){
+ $scope.get_data();
+ });
+ $scope.get_data();
+
+ };
+
+ $scope.get_data = function() {
+ // Make sure we have everything for the request to complete
+ if(dashboard.indices.length === 0) {
+ return;
+ }
+
+ $scope.panelMeta.loading = true;
+ var request,
+ builder,
+ results,
+ boolQuery,
+ queries;
+
+ $scope.field = _.contains(fields.list,$scope.panel.field+'.raw') ?
+ $scope.panel.field+'.raw' : $scope.panel.field;
+
+ request = $scope.ejs.Request().indices(dashboard.indices);
+
+ $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
+ queries = querySrv.getQueryObjs($scope.panel.queries.ids);
+
+ // This could probably be changed to a BoolFilter
+ boolQuery = $scope.ejs.BoolQuery();
+ _.each(queries,function(q) {
+ boolQuery = boolQuery.should(querySrv.toEjsObj(q));
+ });
+
+ // Terms mode
+ if($scope.panel.tmode === 'terms') {
+ builder = $scope.ejs.TermsFacet('terms')
+ .field($scope.field)
+ .size($scope.panel.size)
+ .order($scope.panel.order)
+ .exclude($scope.panel.exclude);
+ }
+ if($scope.panel.tmode === 'terms_stats') {
+ builder = $scope.ejs.TermStatsFacet('terms')
+ .valueField($scope.panel.valuefield)
+ .keyField($scope.field)
+ .size($scope.panel.size)
+ .order($scope.panel.order);
+ }
+
+ builder = builder.facetFilter($scope.ejs.QueryFilter(
+ $scope.ejs.FilteredQuery(
+ boolQuery,
+ $scope.panel.locked ? null : filterSrv.getBoolFilter(filterSrv.ids())
+ )));
+
+ request = request.facet(builder).size(0);
+
+ // Populate the inspector panel
+ $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
+
+ results = request.doSearch();
+
+ // Populate scope when we have results
+ results.then(function(results) {
+ $scope.panelMeta.loading = false;
+ if($scope.panel.tmode === 'terms') {
+ $scope.hits = results.hits.total;
+ }
+
+ $scope.results = results;
+
+ $scope.$emit('render');
+ });
+ };
+
+ $scope.build_search = function(term,negate) {
+ if(_.isUndefined(term.meta)) {
+ filterSrv.set({type:'terms',field:$scope.field,value:term.label,
+ mandate:(negate ? 'mustNot':'must')});
+ } else if(term.meta === 'missing') {
+ filterSrv.set({type:'exists',field:$scope.field,
+ mandate:(negate ? 'must':'mustNot')});
+ } else {
+ return;
+ }
+ };
+
+ $scope.set_refresh = function (state) {
+ $scope.refresh = state;
+ };
+
+ $scope.close_edit = function() {
+ if($scope.refresh) {
+ $scope.get_data();
+ }
+ $scope.refresh = false;
+ $scope.$emit('render');
+ };
+
+ $scope.showMeta = function(term) {
+ if(_.isUndefined(term.meta)) {
+ return true;
+ }
+ if(term.meta === 'other' && !$scope.panel.other) {
+ return false;
+ }
+ if(term.meta === 'missing' && !$scope.panel.missing) {
+ return false;
+ }
+ return true;
+ };
+
+ });
+
+ module.directive('termsChart', function(querySrv) {
+ return {
+ restrict: 'A',
+ link: function(scope, elem) {
+
+ // Receive render events
+ scope.$on('render',function(){
+ render_panel();
+ });
+
+ // Re-render if the window is resized
+ angular.element(window).bind('resize', function(){
+ render_panel();
+ });
+
+ function build_results() {
+ var k = 0;
+ scope.data = [];
+ _.each(scope.results.facets.terms.terms, function(v) {
+ var slice;
+ if(scope.panel.tmode === 'terms') {
+ slice = { label : v.term, data : [[k,v.count]], actions: true};
+ }
+ if(scope.panel.tmode === 'terms_stats') {
+ slice = { label : v.term, data : [[k,v[scope.panel.tstat]]], actions: true};
+ }
+ scope.data.push(slice);
+ k = k + 1;
+ });
+
+ scope.data.push({label:'Missing field',
+ data:[[k,scope.results.facets.terms.missing]],meta:"missing",color:'#aaa',opacity:0});
+
+ if(scope.panel.tmode === 'terms') {
+ scope.data.push({label:'Other values',
+ data:[[k+1,scope.results.facets.terms.other]],meta:"other",color:'#444'});
+ }
+ }
+
+ // Function for rendering panel
+ function render_panel() {
+ var plot, chartData;
+
+ build_results();
+
+ // IE doesn't work without this
+ elem.css({height:scope.row.height});
+
+ // Make a clone we can operate on.
+ chartData = _.clone(scope.data);
+ chartData = scope.panel.missing ? chartData :
+ _.without(chartData,_.findWhere(chartData,{meta:'missing'}));
+ chartData = scope.panel.other ? chartData :
+ _.without(chartData,_.findWhere(chartData,{meta:'other'}));
+
+ // Populate element.
+ require(['jquery.flot.pie'], function(){
+ // Populate element
+ try {
+ // Add plot to scope so we can build out own legend
+ if(scope.panel.chart === 'bar') {
+ plot = $.plot(elem, chartData, {
+ legend: { show: false },
+ series: {
+ lines: { show: false, },
+ bars: { show: true, fill: 1, barWidth: 0.8, horizontal: false },
+ shadowSize: 1
+ },
+ yaxis: { show: true, min: 0, color: "#c8c8c8" },
+ xaxis: { show: false },
+ grid: {
+ borderWidth: 0,
+ borderColor: '#c8c8c8',
+ color: "#c8c8c8",
+ hoverable: true,
+ clickable: true
+ },
+ colors: querySrv.colors
+ });
+ }
+ if(scope.panel.chart === 'pie') {
+ var labelFormat = function(label, series){
+ return '<div ng-click="build_search(panel.field,\''+label+'\')'+
+ ' "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+ label+'<br/>'+Math.round(series.percent)+'%</div>';
+ };
+
+ plot = $.plot(elem, chartData, {
+ legend: { show: false },
+ series: {
+ pie: {
+ innerRadius: scope.panel.donut ? 0.4 : 0,
+ tilt: scope.panel.tilt ? 0.45 : 1,
+ radius: 1,
+ show: true,
+ combine: {
+ color: '#999',
+ label: 'The Rest'
+ },
+ stroke: {
+ width: 0
+ },
+ label: {
+ show: scope.panel.labels,
+ radius: 2/3,
+ formatter: labelFormat,
+ threshold: 0.1
+ }
+ }
+ },
+ //grid: { hoverable: true, clickable: true },
+ grid: { hoverable: true, clickable: true, color: '#c8c8c8' },
+ colors: querySrv.colors
+ });
+ }
+
+ // Populate legend
+ if(elem.is(":visible")){
+ setTimeout(function(){
+ scope.legend = plot.getData();
+ if(!scope.$$phase) {
+ scope.$apply();
+ }
+ });
+ }
+
+ } catch(e) {
+ elem.text(e);
+ }
+ });
+ }
+
+ elem.bind("plotclick", function (event, pos, object) {
+ if(object) {
+ scope.build_search(scope.data[object.seriesIndex]);
+ }
+ });
+
+ var $tooltip = $('<div>');
+ elem.bind("plothover", function (event, pos, item) {
+ if (item) {
+ var value = scope.panel.chart === 'bar' ? item.datapoint[1] : item.datapoint[1][0][1];
+ $tooltip
+ .html(
+ kbn.query_color_dot(item.series.color, 20) + ' ' +
+ item.series.label + " (" + value.toFixed(0)+")"
+ )
+ .place_tt(pos.pageX, pos.pageY);
+ } else {
+ $tooltip.remove();
+ }
+ });
+
+ }
+ };
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/test-chart/module.js
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/test-chart/module.js b/opensoc-ui/lib/public/app/panels/test-chart/module.js
new file mode 100644
index 0000000..e0424ca
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/test-chart/module.js
@@ -0,0 +1,414 @@
+/** @scratch /panels/5
+ *
+ * include::panels/test.asciidoc[]
+ */
+
+/** @scratch /panels/test/0
+ *
+ * == terms
+ * Status: *Stable*
+ *
+ * A pie chart based on the results of an ES.
+ *
+ */
+define([
+ 'angular',
+ 'app',
+ 'lodash',
+ 'jquery',
+ 'kbn'
+],
+function (angular, app, _, $, kbn) {
+ 'use strict';
+
+ var module = angular.module('kibana.panels.tests', []);
+ app.useModule(module);
+
+ module.controller('tests', function($scope, querySrv, dashboard, filterSrv, fields) {
+ $scope.panelMeta = {
+ modals : [
+ {
+ description: "Inspect",
+ icon: "icon-info-sign",
+ partial: "app/partials/inspector.html",
+ show: $scope.panel.spyable
+ }
+ ],
+ editorTabs : [
+ {title:'Queries', src:'app/partials/querySelect.html'}
+ ],
+ status : "Stable",
+ description : "Displays the results of an elasticsearch facet as a pie chart, bar chart, or a "+
+ "table"
+ };
+
+ // Set and populate defaults
+ var _d = {
+ /** @scratch /panels/terms/5
+ * === Parameters
+ *
+ * field:: The field on which to computer the facet
+ */
+ field : '_type',
+ /** @scratch /panels/terms/5
+ * exclude:: terms to exclude from the results
+ */
+ exclude : [],
+ /** @scratch /panels/terms/5
+ * missing:: Set to false to disable the display of a counter showing how much results are
+ * missing the field
+ */
+ missing : true,
+ /** @scratch /panels/terms/5
+ * other:: Set to false to disable the display of a counter representing the aggregate of all
+ * values outside of the scope of your +size+ property
+ */
+ other : true,
+ /** @scratch /panels/terms/5
+ * size:: Show this many terms
+ */
+ size : 10,
+ /** @scratch /panels/terms/5
+ * order:: In terms mode: count, term, reverse_count or reverse_term,
+ * in terms_stats mode: term, reverse_term, count, reverse_count,
+ * total, reverse_total, min, reverse_min, max, reverse_max, mean or reverse_mean
+ */
+ order : 'count',
+ style : { "font-size": '10pt'},
+ /** @scratch /panels/terms/5
+ * donut:: In pie chart mode, draw a hole in the middle of the pie to make a tasty donut.
+ */
+ donut : false,
+ /** @scratch /panels/terms/5
+ * tilt:: In pie chart mode, tilt the chart back to appear as more of an oval shape
+ */
+ tilt : false,
+ /** @scratch /panels/terms/5
+ * lables:: In pie chart mode, draw labels in the pie slices
+ */
+ labels : true,
+ /** @scratch /panels/terms/5
+ * arrangement:: In bar or pie mode, arrangement of the legend. horizontal or vertical
+ */
+ arrangement : 'horizontal',
+ /** @scratch /panels/terms/5
+ * chart:: table, bar or pie
+ */
+ chart : 'bar',
+ /** @scratch /panels/terms/5
+ * counter_pos:: The location of the legend in respect to the chart, above, below, or none.
+ */
+ counter_pos : 'above',
+ /** @scratch /panels/terms/5
+ * spyable:: Set spyable to false to disable the inspect button
+ */
+ spyable : true,
+ /** @scratch /panels/terms/5
+ *
+ * ==== Queries
+ * queries object:: This object describes the queries to use on this panel.
+ * queries.mode::: Of the queries available, which to use. Options: +all, pinned, unpinned, selected+
+ * queries.ids::: In +selected+ mode, which query ids are selected.
+ */
+ queries : {
+ mode : 'all',
+ ids : []
+ },
+
+ /*
+ * locked:: whether to lock the query, preventing it from being affected by filters
+ */
+ locked: false,
+ /** @scratch /panels/terms/5
+ * tmode:: Facet mode: terms or terms_stats
+ */
+ tmode : 'terms',
+ /** @scratch /panels/terms/5
+ * tstat:: Terms_stats facet stats field
+ */
+ tstat : 'total',
+ /** @scratch /panels/terms/5
+ * valuefield:: Terms_stats facet value field
+ */
+ valuefield : ''
+ };
+
+ _.defaults($scope.panel,_d);
+
+ $scope.init = function () {
+ $scope.hits = 0;
+
+ // Static Chart
+ // $scope.$on('refresh',function(){
+ // $scope.get_data();
+ // });
+ $scope.get_data();
+
+ };
+
+ $scope.get_data = function() {
+ // Make sure we have everything for the request to complete
+ if(dashboard.indices.length === 0) {
+ return;
+ }
+
+ $scope.panelMeta.loading = true;
+ var request,
+ builder,
+ results,
+ boolQuery,
+ queries;
+
+ $scope.field = _.contains(fields.list,$scope.panel.field+'.raw') ?
+ $scope.panel.field+'.raw' : $scope.panel.field;
+
+ request = $scope.ejs.Request().indices(dashboard.indices);
+
+ $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
+ queries = querySrv.getQueryObjs($scope.panel.queries.ids);
+
+ // This could probably be changed to a BoolFilter
+ boolQuery = $scope.ejs.BoolQuery();
+ _.each(queries,function(q) {
+ boolQuery = boolQuery.should(querySrv.toEjsObj(q));
+ });
+
+ // Terms mode
+ if($scope.panel.tmode === 'terms') {
+ builder = $scope.ejs.TermsFacet('terms')
+ .field($scope.field)
+ .size($scope.panel.size)
+ .order($scope.panel.order)
+ .exclude($scope.panel.exclude);
+ }
+ if($scope.panel.tmode === 'terms_stats') {
+ builder = $scope.ejs.TermStatsFacet('terms')
+ .valueField($scope.panel.valuefield)
+ .keyField($scope.field)
+ .size($scope.panel.size)
+ .order($scope.panel.order);
+ }
+
+ builder = builder.facetFilter($scope.ejs.QueryFilter(
+ $scope.ejs.FilteredQuery(
+ boolQuery,
+ $scope.panel.locked ? null : filterSrv.getBoolFilter(filterSrv.ids())
+ )));
+
+ request = request.facet(builder).size(0);
+
+ // Populate the inspector panel
+ $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
+
+ results = request.doSearch();
+
+ // Populate scope when we have results
+ results.then(function(results) {
+ $scope.panelMeta.loading = false;
+ if($scope.panel.tmode === 'terms') {
+ $scope.hits = results.hits.total;
+ }
+
+ $scope.results = results;
+
+ $scope.$emit('render');
+ });
+ };
+
+ $scope.build_search = function(term,negate) {
+ if(_.isUndefined(term.meta)) {
+ filterSrv.set({type:'terms',field:$scope.field,value:term.label,
+ mandate:(negate ? 'mustNot':'must')});
+ } else if(term.meta === 'missing') {
+ filterSrv.set({type:'exists',field:$scope.field,
+ mandate:(negate ? 'must':'mustNot')});
+ } else {
+ return;
+ }
+ };
+
+ $scope.set_refresh = function (state) {
+ $scope.refresh = state;
+ };
+
+ $scope.close_edit = function() {
+ if($scope.refresh) {
+ $scope.get_data();
+ }
+ $scope.refresh = false;
+ $scope.$emit('render');
+ };
+
+ $scope.showMeta = function(term) {
+ if(_.isUndefined(term.meta)) {
+ return true;
+ }
+ if(term.meta === 'other' && !$scope.panel.other) {
+ return false;
+ }
+ if(term.meta === 'missing' && !$scope.panel.missing) {
+ return false;
+ }
+ return true;
+ };
+
+ });
+
+ module.directive('termsChart', function(querySrv) {
+ return {
+ restrict: 'A',
+ link: function(scope, elem) {
+
+ // Receive render events
+ scope.$on('render',function(){
+ render_panel();
+ });
+
+ // Re-render if the window is resized
+ angular.element(window).bind('resize', function(){
+ render_panel();
+ });
+
+ function build_results() {
+ var k = 0;
+ scope.data = [];
+ _.each(scope.results.facets.terms.terms, function(v) {
+ var slice;
+ if(scope.panel.tmode === 'terms') {
+ slice = { label : v.term, data : [[k,v.count]], actions: true};
+ }
+ if(scope.panel.tmode === 'terms_stats') {
+ slice = { label : v.term, data : [[k,v[scope.panel.tstat]]], actions: true};
+ }
+ scope.data.push(slice);
+ k = k + 1;
+ });
+
+ scope.data.push({label:'Missing field',
+ data:[[k,scope.results.facets.terms.missing]],meta:"missing",color:'#aaa',opacity:0});
+
+ if(scope.panel.tmode === 'terms') {
+ scope.data.push({label:'Other values',
+ data:[[k+1,scope.results.facets.terms.other]],meta:"other",color:'#444'});
+ }
+ }
+
+ // Function for rendering panel
+ function render_panel() {
+ var plot, chartData;
+
+ build_results();
+
+ // IE doesn't work without this
+ elem.css({height:scope.row.height});
+
+ // Make a clone we can operate on.
+ chartData = _.clone(scope.data);
+ chartData = scope.panel.missing ? chartData :
+ _.without(chartData,_.findWhere(chartData,{meta:'missing'}));
+ chartData = scope.panel.other ? chartData :
+ _.without(chartData,_.findWhere(chartData,{meta:'other'}));
+
+ // Populate element.
+ require(['jquery.flot.pie'], function(){
+ // Populate element
+ try {
+ // Add plot to scope so we can build out own legend
+ if(scope.panel.chart === 'bar') {
+ plot = $.plot(elem, chartData, {
+ legend: { show: false },
+ series: {
+ lines: { show: false, },
+ bars: { show: true, fill: 1, barWidth: 0.8, horizontal: false },
+ shadowSize: 1
+ },
+ yaxis: { show: true, min: 0, color: "#c8c8c8" },
+ xaxis: { show: false },
+ grid: {
+ borderWidth: 0,
+ borderColor: '#c8c8c8',
+ color: "#c8c8c8",
+ hoverable: true,
+ clickable: true
+ },
+ colors: querySrv.colors
+ });
+ }
+ if(scope.panel.chart === 'pie') {
+ var labelFormat = function(label, series){
+ return '<div ng-click="build_search(panel.field,\''+label+'\')'+
+ ' "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+ label+'<br/>'+Math.round(series.percent)+'%</div>';
+ };
+
+ plot = $.plot(elem, chartData, {
+ legend: { show: false },
+ series: {
+ pie: {
+ innerRadius: scope.panel.donut ? 0.4 : 0,
+ tilt: scope.panel.tilt ? 0.45 : 1,
+ radius: 1,
+ show: true,
+ combine: {
+ color: '#999',
+ label: 'The Rest'
+ },
+ stroke: {
+ width: 0
+ },
+ label: {
+ show: scope.panel.labels,
+ radius: 2/3,
+ formatter: labelFormat,
+ threshold: 0.1
+ }
+ }
+ },
+ //grid: { hoverable: true, clickable: true },
+ grid: { hoverable: true, clickable: true, color: '#fff' },
+ colors: querySrv.colors
+ });
+ }
+
+ // Populate legend
+ if(elem.is(":visible")){
+ setTimeout(function(){
+ scope.legend = plot.getData();
+ if(!scope.$$phase) {
+ scope.$apply();
+ }
+ });
+ }
+
+ } catch(e) {
+ elem.text(e);
+ }
+ });
+ }
+
+ elem.bind("plotclick", function (event, pos, object) {
+ if(object) {
+ scope.build_search(scope.data[object.seriesIndex]);
+ }
+ });
+
+ var $tooltip = $('<div>');
+ elem.bind("plothover", function (event, pos, item) {
+ if (item) {
+ var value = scope.panel.chart === 'bar' ? item.datapoint[1] : item.datapoint[1][0][1];
+ $tooltip
+ .html(
+ kbn.query_color_dot(item.series.color, 20) + ' ' +
+ item.series.label + " (" + value.toFixed(0)+")"
+ )
+ .place_tt(pos.pageX, pos.pageY);
+ } else {
+ $tooltip.remove();
+ }
+ });
+
+ }
+ };
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/05e188ba/opensoc-ui/lib/public/app/panels/text/editor.html
----------------------------------------------------------------------
diff --git a/opensoc-ui/lib/public/app/panels/text/editor.html b/opensoc-ui/lib/public/app/panels/text/editor.html
new file mode 100755
index 0000000..02177bc
--- /dev/null
+++ b/opensoc-ui/lib/public/app/panels/text/editor.html
@@ -0,0 +1,16 @@
+<div>
+ <div class="row-fluid">
+ <div class="span4">
+ <label class="small">Mode</label> <select class="input-medium" ng-model="panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
+ </div>
+ <div class="span2" ng-show="panel.mode == 'text'">
+ <label class="small">Font Size</label> <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select>
+ </div>
+ </div>
+
+ <label class=small>Content
+ <span ng-show="panel.mode == 'html'">(This area uses HTML sanitized via AngularJS's <a href='http://docs.angularjs.org/api/ngSanitize.$sanitize'>$sanitize</a> service)</span>
+ <span ng-show="panel.mode == 'markdown'">(This area uses <a target="_blank" href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>. HTML is not supported)</span>
+ </label>
+ <textarea ng-model="panel.content" rows="6" style="width:95%"></textarea>
+</div>
\ No newline at end of file