PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/public/lib/js/ajax.js

https://github.com/shellcode/Kibana
JavaScript | 1724 lines | 1430 code | 187 blank | 107 comment | 156 complexity | 456b9dd8b691df3330ba205bb7648d8d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. $(document).ready(function () {
  2. // Bind all click/change/whatever handlers
  3. bind_clicks()
  4. popover_setup()
  5. // Common color profile.
  6. window.graph_colors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"]
  7. // Hide sidebar by default
  8. sbctl('hide',false);
  9. // Handle AJAX errors
  10. $("div#logs").ajaxError(function (e, xhr, settings, exception) {
  11. $('#meta').text("");
  12. if(xhr.statusText != 'abort') {
  13. showError("<strong>Oops!</strong> Something went terribly wrong.",
  14. "I'm not totally sure what happened, but maybe refreshing, or "+
  15. "hitting Reset will help. If that doesn't work, you can try "+
  16. "restarting your browser. If all else fails, it is possible your"+
  17. " configuation has something funky going on. <br><br>If it helps,"+
  18. " I received a <strong>" + xhr.status + " " + xhr.statusText +
  19. "</strong> from: " + settings.url);
  20. }
  21. });
  22. // Whenever the URL changes, fire this.
  23. $.history.init(pageload);
  24. // Resize flot graph with window
  25. $(window).smartresize(function () {
  26. if ($(".legend").length > 0){
  27. logGraph(window.graphdata,window.interval,window.hashjson.graphmode);
  28. }
  29. });
  30. });
  31. // This gets called every time the URL changes,
  32. // Including hash changes, setHash() will
  33. // cause a reload of the results
  34. function pageload(hash) {
  35. if (typeof window.request !== 'undefined') {
  36. window.request.abort();
  37. window.segment = undefined;
  38. try { delete window.segment; } catch (e) {}
  39. }
  40. //if hash value exists, run the ajax
  41. if (hash) {
  42. window.hashjson = JSON.parse(Base64.decode(hash));
  43. //console.log(window.hashjson)
  44. // Take the hash data and populate the search fields
  45. $('#queryinput').val(window.hashjson.search);
  46. $('#timeinput').val(window.hashjson.timeframe);
  47. // Figure out defaults. Need more here
  48. if(typeof window.hashjson.graphmode == 'undefined')
  49. window.hashjson.graphmode = 'count';
  50. if(typeof window.hashjson.time == 'undefined')
  51. window.hashjson.time = {user_interval:0};
  52. else if (typeof window.hashjson.time.user_interval == 'undefined')
  53. window.hashjson.time.user_interval = 0;
  54. switch (window.hashjson.mode) {
  55. case 'terms':
  56. case 'score':
  57. case 'trend':
  58. case 'mean':
  59. getAnalysis();
  60. break;
  61. case 'id':
  62. getID();
  63. break;
  64. default:
  65. $('#feedlinks').html(feedLinks(window.hashjson));
  66. getPage();
  67. break;
  68. }
  69. } else {
  70. resetAll();
  71. }
  72. }
  73. function getPage() {
  74. if (window.inprogress) {
  75. return false;
  76. }
  77. window.inprogress = true;
  78. // Show the user an animated loading thingy
  79. setMeta('loading');
  80. var sendhash = encodeURIComponent(window.location.hash.replace(/^#/, ''));
  81. //Get the data and display it
  82. window.request = $.ajax({
  83. url: "api/search/"+sendhash,
  84. type: "GET",
  85. cache: false,
  86. success: function (json) {
  87. // Make sure we're still on the same page
  88. if (sendhash == encodeURIComponent(window.location.hash.replace(/^#/, ''))) {
  89. //Parse out the result
  90. window.resultjson = JSON.parse(json);
  91. //console.log(
  92. // 'curl -XGET \'http://localhost:9200/'+resultjson.index+
  93. // '/_search?pretty=true\' -d\''+resultjson.kibana.es_query+'\'');
  94. //console.log(resultjson.kibana.curl_call);
  95. $('#graphheader,#graph').text("");
  96. $('#feedlinks').html(feedLinks(window.hashjson));
  97. // Make sure we get some results before doing anything
  98. if ((typeof window.resultjson.kibana.error !== 'undefined') || (!(resultjson.hits))) {
  99. setMeta(0);
  100. showError('No events matched',"Sorry, I couldn't find anything for " +
  101. "that query. Double check your spelling and syntax.");
  102. return;
  103. }
  104. if (!(resultjson.hits.total > 0)) {
  105. setMeta(0);
  106. showError('No events matched',"Sorry, I couldn't find anything for " +
  107. "that query. Double check your spelling and syntax.");
  108. return;
  109. }
  110. // Determine fields to be displayed
  111. var fields = window.hashjson.fields.length == 0 ?
  112. resultjson.kibana.default_fields : window.hashjson.fields
  113. // Create 'Columns' section
  114. $('#fields').html("<div class='input-prepend'>" +
  115. "<span class='add-on'><i class='icon-columns'></i></span>" +
  116. "<input id='field_filter' type='text' class='span' placeholder='Columns' /></div>" +
  117. "<ul class='unselected nav nav-pills nav-stacked'></ul>");
  118. var all_fields = array_unique(get_all_fields(resultjson).concat(fields))
  119. // Create sidebar field list
  120. var fieldstr = '';
  121. for (var index in all_fields) {
  122. var field_name = all_fields[index].toString();
  123. var afield = field_alias(field_name) + "_field";
  124. var mode = $.inArray(field_name,window.hashjson.fields) >= 0 ?
  125. 'selected' : 'unselected'
  126. fieldstr += sidebar_field_string(field_name,mode);
  127. }
  128. $('#fields ul.unselected').append(fieldstr)
  129. // Store list of fields for filter use
  130. window.field_list = $('#fields > ul > li');
  131. //var fieldstr = '';
  132. //for (var index in window.hashjson.fields) {
  133. // var field_name = window.hashjson.fields[index].toString();
  134. // var afield = field_alias(field_name) + "_field";
  135. // $('#fields ul.unselected li.' + afield).hide();
  136. // fieldstr += sidebar_field_string(field_name,'minus');
  137. //}
  138. //$('#fields ul.selected').append(fieldstr)
  139. enable_popovers();
  140. // Create and populate #logs table
  141. $('#logs').html(CreateLogTable(
  142. window.resultjson.hits.hits, fields,
  143. 'table logs table-condensed'
  144. ));
  145. pageLinks();
  146. // Populate hit and total
  147. setMeta(window.resultjson.hits.total);
  148. window.interval = calculate_interval(
  149. Date.parse(window.resultjson.kibana.time.from),
  150. Date.parse(window.resultjson.kibana.time.to),
  151. 100,
  152. window.hashjson.time.user_interval
  153. )
  154. if (typeof window.sb == 'undefined') {
  155. sbctl('show',false);
  156. } else {
  157. sbctl(window.sb,false);
  158. }
  159. // Create and populate graph
  160. $('#graph').html(
  161. '<center><br><p><img src=images/barload.gif></center>');
  162. getGraph(window.interval);
  163. }
  164. }
  165. });
  166. window.inprogress = false;
  167. }
  168. function getGraph(interval) {
  169. //generate the parameter for the php script
  170. var sendhash = encodeURIComponent(window.location.hash.replace(/^#/, ''));
  171. var mode = window.hashjson.graphmode;
  172. window.segment = typeof window.segment === 'undefined' ? '' : window.segment;
  173. //Get the data and display it
  174. window.request = $.ajax({
  175. url: "api/graph/"+mode+"/"+interval+"/"+sendhash+"/"+window.segment,
  176. type: "GET",
  177. cache: false,
  178. success: function (json) {
  179. // Make sure we're still on the same page
  180. if (sendhash == encodeURIComponent(window.location.hash.replace(/^#/, ''))) {
  181. //Parse out the returned JSON
  182. var graphjson = JSON.parse(json);
  183. if ($(".legend").length > 0) {
  184. window.graphdata = graphjson.facets[mode].entries.concat(
  185. window.graphdata);
  186. window.graphhits = graphjson.hits.total + window.graphhits;
  187. } else {
  188. window.graphdata = graphjson.facets[mode].entries;
  189. window.graphhits = graphjson.hits.total;
  190. }
  191. setMeta(window.graphhits);
  192. // Display graph data
  193. logGraph(window.graphdata, interval, mode);
  194. if (typeof graphjson.kibana.next !== 'undefined') {
  195. window.segment = graphjson.kibana.next;
  196. if (!($(".graphloading").length > 0)) {
  197. $('div.legend table, div.legend table td').css({
  198. "background-image": "url(images/barload.gif)",
  199. "background-size": "100% 100%"
  200. });
  201. }
  202. getGraph(interval);
  203. } else {
  204. if(typeof window.segment !== 'undefined')
  205. window['segment'] = undefined;
  206. try { delete window['segment'] } catch (e) {}
  207. }
  208. }
  209. }
  210. });
  211. }
  212. // create a pie chart for a terms facet
  213. function pieChart(data,selector){
  214. var plot = $.plot($(selector), data, {
  215. series: {
  216. pie: {
  217. radius: 1,
  218. show: true,
  219. combine: {
  220. color: '#999',
  221. label: 'The Rest'
  222. },
  223. label: { show: false }
  224. }
  225. },
  226. grid: { hoverable: true, clickable: true },
  227. legend: { show: false }
  228. });
  229. // This should be generalized. Too much copy + paste
  230. var previousLabel = null;
  231. $(selector).bind("plothover", function (event, pos, item) {
  232. if (item) {
  233. if (previousLabel != item.series.label) {
  234. previousLabel = item.series.label;
  235. $("#tooltip").remove();
  236. var label = (!item.series.label) ? "missing" : item.series.label;
  237. showTooltip(
  238. pos.pageX+30, pos.pageY,
  239. "<b>"+label+"</b>" + " " + Math.round(item.series.percent) + "%"
  240. );
  241. }
  242. } else {
  243. $("#tooltip").remove();
  244. previousLabel = null;
  245. }
  246. });
  247. }
  248. function analyzeField(field, mode) {
  249. window.hashjson.mode = mode;
  250. window.hashjson.analyze_field = field;
  251. setHash(window.hashjson);
  252. }
  253. function getID() {
  254. // Show the user an animated loading thingy
  255. var sendhash = encodeURIComponent(window.location.hash.replace(/^#/, ''));
  256. //Get the data and display it
  257. window.request = $.ajax({
  258. url: "api/id/"+window.hashjson.id+"/"+window.hashjson.index,
  259. type: "GET",
  260. cache: false,
  261. success: function (json) {
  262. window.resultjson = JSON.parse(json)
  263. var hit = resultjson.hits.hits[0]
  264. blank_page();
  265. setMeta(1);
  266. var str = details_table(0, 'table table-bordered');
  267. $('#graph').html(
  268. "<h2>Details for log ID: "+hit._id+" in "+hit._index+"</h2><br>"+str);
  269. }
  270. });
  271. sbctl('hide',false)
  272. window.hashjson.id = undefined;
  273. window.hashjson.index = undefined
  274. window.hashjson.mode = undefined
  275. try{
  276. delete window.hashjson.id;
  277. delete window.hashjson.index
  278. delete window.hashjson.mode
  279. }catch(e){}
  280. }
  281. function getAnalysis() {
  282. setMeta('loading');
  283. //generate the parameter for the php script
  284. var sendhash = encodeURIComponent(window.location.hash.replace(/^#/, ''));
  285. //Get the data and display it
  286. window.request = $.ajax({
  287. url: "api/analyze/" + window.hashjson.analyze_field + "/" +
  288. window.hashjson.mode + "/" + sendhash,
  289. type: "GET",
  290. cache: false,
  291. success: function (json) {
  292. // Make sure we're still on the same page
  293. if (sendhash == encodeURIComponent(window.location.hash.replace(/^#/, ''))) {
  294. //Parse out the returned JSON
  295. var field = window.hashjson.analyze_field;
  296. resultjson = JSON.parse(json);
  297. $('.pagelinks').html('');
  298. $('#fields').html('');
  299. if(typeof resultjson.error !== 'undefined') {
  300. setMeta(0);
  301. showError('Statistical analysis unavailable for '+field +
  302. ' <button class="btn tiny btn-info" ' +
  303. 'style="display: inline-block" id="back_to_logs">back to logs' +
  304. '</button>',
  305. "I'm not able to analyze <strong>" + field + "</strong>. " +
  306. "Statistical analysis is only available for fields " +
  307. "that are stored a number (eg float, int) in ElasticSearch");
  308. return;
  309. }
  310. window.interval = calculate_interval(
  311. Date.parse(window.resultjson.kibana.time.from),
  312. Date.parse(window.resultjson.kibana.time.to),
  313. 100,
  314. window.hashjson.time.user_interval
  315. )
  316. if(resultjson.hits.total == 0) {
  317. setMeta(resultjson.hits.total);
  318. showError('No events matched '+
  319. '<button class="btn tiny btn-info" ' +
  320. 'style="display: inline-block" id="back_to_logs">back to logs' +
  321. '</button>',
  322. "Sorry, I couldn't find anything for " +
  323. "that query. Double check your spelling and syntax.");
  324. return;
  325. }
  326. setMeta(resultjson.hits.total);
  327. var analyze_field = window.hashjson.analyze_field.split(',,').join(' ');
  328. switch (window.hashjson.mode) {
  329. case 'terms':
  330. var index_count = $.isArray(resultjson.kibana.index) ?
  331. resultjson.kibana.index : resultjson.kibana.index.split(',').length;
  332. // add the missing count for the terms table and pie chart
  333. // This should really insert at the right point.
  334. resultjson.facets.terms.terms.push({
  335. term: '',
  336. count: resultjson.facets.terms.missing
  337. });
  338. var title = ''+
  339. '<h2>Terms Facet of ' +
  340. '<strong>' + analyze_field + '</strong> field(s) ' +
  341. '<button class="btn tiny btn-info" ' +
  342. 'style="display: inline-block" id="back_to_logs">back to logs' +
  343. '</button>' +
  344. '<div class=pull-left id="piechart"></div></h2>' +
  345. 'This analysis is based on the events in the <strong>' +
  346. index_count +'</strong> most recent indices ' +
  347. 'for your query in your selected timeframe.<br><br>'+
  348. '';
  349. $('#logs').html(
  350. title+CreateTableView(termsTable(resultjson),'logs analysis'));
  351. sbctl('hide',false)
  352. graphLoading();
  353. window.hashjson.graphmode = 'count'
  354. getGraph(window.interval);
  355. // Calculate data for pie chart
  356. var data = [];
  357. $.each(resultjson.facets.terms.terms,function(i,term) {
  358. data[i] = { label: term['term'], data: term['count'], color: window.graph_colors[i] };
  359. });
  360. var remain = data.slice(window.graph_colors.length,data.length)
  361. var r = 0
  362. for (var x in remain) { r += remain[x].data; }
  363. data = data.slice(0,window.graph_colors.length)
  364. data.push({ label: "The Rest", data: r, color: '#AAA' })
  365. pieChart(data,'#piechart')
  366. break;
  367. case 'score':
  368. if (resultjson.hits.count == resultjson.hits.total) {
  369. var basedon = "<strong>all "
  370. + resultjson.hits.count + "</strong>"
  371. } else {
  372. var basedon = 'the <strong>' +
  373. resultjson.hits.count + ' most recent</strong>';
  374. }
  375. var title = '<h2>Quick analysis of ' +
  376. '<strong>' + analyze_field + '</strong> field(s) ' +
  377. '<button class="btn tiny btn-info" ' +
  378. 'style="display: inline-block" id="back_to_logs">back to logs' +
  379. '</button>' +
  380. '</h2>' +
  381. 'This analysis is based on ' + basedon +
  382. ' events for your query in your selected timeframe.<br><br>';
  383. $('#logs').html(
  384. title+CreateTableView(analysisTable(resultjson),'logs analysis'));
  385. sbctl('hide',false)
  386. graphLoading();
  387. window.hashjson.graphmode = 'count'
  388. getGraph(window.interval);
  389. break;
  390. case 'trend':
  391. var basedon = "<strong>" + resultjson.hits.count + "</strong>";
  392. var title = '<h2>Trend analysis of <strong>' +
  393. analyze_field + '</strong> field ' +
  394. '<button class="btn tiny btn-info" ' +
  395. 'style="display: inline-block" id="back_to_logs">back to logs' +
  396. '</button>' +
  397. '</h2>' +
  398. 'These trends are based on ' + basedon + ' events from beginning' +
  399. ' and end of the selected timeframe for your query.<br><br>';
  400. $('#logs').html(
  401. title+CreateTableView(analysisTable(resultjson),'logs analysis'));
  402. sbctl('hide',false)
  403. graphLoading();
  404. window.hashjson.graphmode = 'count'
  405. getGraph(window.interval);
  406. break;
  407. case 'mean':
  408. var title = '<h2>Statistical analysis of <strong>' +
  409. analyze_field + '</strong> field ' +
  410. '<button class="btn tiny btn-info" ' +
  411. 'style="display: inline-block" id="back_to_logs">back to logs' +
  412. '</button>' +
  413. '</h2>' +
  414. 'Simple computations of a numeric field across your timeframe. ' +
  415. 'The graph above <strong>shows the mean value</strong> ' +
  416. 'of the <strong>'+field+
  417. '</strong> field over your selected time frame' +
  418. '<br><br>';
  419. var tbl = Array(), i = 0, metric;
  420. resultjson.facets.stats._type = undefined;
  421. try{
  422. delete resultjson.facets.stats._type
  423. }catch(e){}
  424. for (var obj in resultjson.facets.stats) {
  425. var metric = Array();
  426. metric['Statistic'] = obj.charAt(0).toUpperCase() + obj.slice(1);
  427. metric['Value'] = resultjson.facets.stats[obj];
  428. tbl[i] = metric;
  429. i++;
  430. }
  431. $('#logs').html(title+CreateTableView(tbl,'logs'));
  432. sbctl('hide',false)
  433. graphLoading();
  434. window.hashjson.graphmode = 'mean'
  435. getGraph(window.interval);
  436. break;
  437. }
  438. }
  439. }
  440. });
  441. }
  442. function graphLoading() {
  443. $('#graph').html(
  444. '<center><br><p><img src=' +
  445. 'images/barload.gif></center>');
  446. }
  447. function analysisTable(resultjson) {
  448. var i = 0;
  449. var tblArray = new Array();
  450. for (var obj in resultjson.hits.hits) {
  451. metric = {},
  452. object = resultjson.hits.hits[obj];
  453. metric['Rank'] = i+1;
  454. var idv = object.id.split('||');
  455. var fields = window.hashjson.analyze_field.split(',,');
  456. for (var count = 0; count < fields.length; count++) {
  457. metric[fields[count]]=xmlEnt(idv[count]);
  458. }
  459. var analyze_field = fields.join(' ')
  460. metric['Count'] = object.count;
  461. metric['Percent'] = Math.round(
  462. metric['Count'] / resultjson.hits.count * 10000
  463. ) / 100 + '%';
  464. if (window.hashjson.mode == 'trend') {
  465. if (object.trend > 0) {
  466. metric['Trend'] = '<span class=positive>+' +
  467. object.trend + '</span>';
  468. } else {
  469. metric['Trend'] = '<span class=negative>' +
  470. object.trend + '</span>';
  471. }
  472. }
  473. metric['Action'] = "<span class='raw'>" + xmlEnt(object.id) + "</span>"+
  474. "<i data-mode='' data-field='" + analyze_field + "' "+
  475. "class='msearch icon-search icon-large jlink'></i> " +
  476. "<i data-mode='analysis' data-field='"+analyze_field+"' "+
  477. "class='msearch icon-cog icon-large jlink'></i>";
  478. tblArray[i] = metric;
  479. i++;
  480. }
  481. return tblArray;
  482. }
  483. function termsTable(resultjson) {
  484. var i = 0;
  485. var tblArray = new Array();
  486. for (var obj in resultjson.facets.terms.terms) {
  487. var object = resultjson.facets.terms.terms[obj];
  488. var metric = {};
  489. var color = i < window.graph_colors.length ?
  490. " <i class=icon-sign-blank style='color:"+window.graph_colors[i]+"'><i>" : '';
  491. metric['Rank'] = (i + 1) + color;
  492. var termv = object.term.split('||');
  493. var fields = window.hashjson.analyze_field.split(',,');
  494. for (var count = 0; count < fields.length; count++) {
  495. // TODO: This is so wrong, really shouldn't be matching a string here
  496. if (typeof termv[count] === 'undefined' || termv[count] == "null" ) {
  497. var value = ''
  498. } else {
  499. var value = xmlEnt(termv[count])
  500. }
  501. metric[fields[count]] = value;
  502. }
  503. var analyze_field = fields.join(' ')
  504. metric['Count'] = addCommas(object.count);
  505. metric['Percent'] = Math.round(
  506. object.count / resultjson.hits.total * 10000
  507. ) / 100 + '%';
  508. metric['Action'] = "<span class='raw'>" + xmlEnt(object.term) + "</span>"+
  509. "<i data-mode='' data-field='" + analyze_field + "' "+
  510. "class='msearch icon-search icon-large jlink'></i> " +
  511. "<i data-mode='analysis' data-field='"+analyze_field+"' "+
  512. "class='msearch icon-cog icon-large jlink'></i>";
  513. tblArray[i] = metric;
  514. i++;
  515. }
  516. return tblArray;
  517. }
  518. function setMeta(hits, mode) {
  519. if ( hits == 'loading' ) {
  520. $('#meta').html('<img src=images/ajax-loader.gif>');
  521. } else {
  522. $('#meta').html(
  523. addCommas(hits) + " <span class=small>hits</span></td></tr>");
  524. }
  525. }
  526. function sidebar_field_string(field, mode) {
  527. var icon = mode == 'selected' ? 'minus' : 'plus';
  528. return '<li data-field="'+field+'">'+
  529. '<i class="small icon-'+icon+' jlink mfield" data-field="'+field+'"></i> '+
  530. '<a style="display:inline-block" class="' + mode +
  531. ' popup-marker jlink field" rel="popover" data-field="'+field+'">'+ field +
  532. "<i class='field icon-caret-right'></i></a></li>";
  533. }
  534. function popover_setup() {
  535. popover_visible = false;
  536. popover_clickedaway = false;
  537. $(document).click(function(e) {
  538. if(popover_visible & popover_clickedaway & !$(e.target).is("a.micro"))
  539. {
  540. $('.popover').remove()
  541. popover_visible = popover_clickedaway = false
  542. } else {
  543. popover_clickedaway = true
  544. }
  545. });
  546. }
  547. function enable_popovers() {
  548. $('.popup-marker').popover({
  549. html: true,
  550. trigger: 'manual',
  551. title: function() {
  552. var field = $(this).attr('data-field');
  553. var objids = get_objids_with_field(window.resultjson,field);
  554. var buttons = "<span class='raw'>" + field + "</span>" +
  555. "<i class='jlink icon-search msearch' data-action='' "+
  556. "data-field='_exists_'></i> " +
  557. "<i class='jlink icon-ban-circle msearch' data-action='' "+
  558. "data-field='_missing_'></i> ";
  559. var str = buttons + " " + field +
  560. " <small><span class='small event_count'>"+
  561. "(<a class='jlink highlight_events' data-field='"+field+"'" +
  562. " data-mode='field' data-objid='"+objids+"'>" +
  563. objids.length+" events</a> on this page)</span></small>";
  564. return str;
  565. },
  566. content: function() {
  567. var related_limit = 10;
  568. var field = $(this).attr('data-field');
  569. var objids = get_objids_with_field(window.resultjson,field);
  570. var counts = get_related_fields(window.resultjson,field);
  571. var str = ''
  572. if(counts.length > 0) {
  573. str = '<span class=related><small><strong>Related fields:</strong><br> '
  574. var i = 0
  575. $.each(counts, function(index,value) {
  576. var display = i < related_limit ? 'inline-block' : 'none';
  577. str += "<span style='display:"+display+"'><a data-field='" + value[0] + "' "+
  578. "class='jlink micro mfield'>" + value[0] +
  579. "</a> (" + to_percent(value[1],objids.length) + "), </span>";
  580. i++;
  581. });
  582. str += (i > related_limit) ? ' <a class="jlink micro more">' +
  583. (i - related_limit) + ' more</a>' : '';
  584. str += "</small></span>";
  585. }
  586. str = microAnalysisTable(window.resultjson,field,5) +
  587. "<span id=micrograph></span>"+
  588. str +
  589. "<div class='btn-group'>" +
  590. "<button class='btn btn-small analyze_btn' rel='score' " +
  591. "data-field="+field+"><i class='icon-list-ol'></i> Score</button>" +
  592. "<button class='btn btn-small analyze_btn' rel='trend' " +
  593. "data-field="+field+"><i class='icon-tasks'></i> Trend</button>" +
  594. "<button class='btn btn-small analyze_btn' rel='terms' " +
  595. "data-field="+field+"><i class='icon-th-list'></i> Terms</button>" +
  596. "<button class='btn btn-small analyze_btn' rel='mean' " +
  597. "data-field="+field+"><i class='icon-bar-chart'></i> Stats</button>" +
  598. "</div>";
  599. return str;
  600. },
  601. }).click(function(e) {
  602. if(popover_visible) {
  603. $('.popover').remove();
  604. }
  605. $(this).popover('show');
  606. var data = top_field_values(window.resultjson,$(this).attr('data-field'),5)
  607. var i = 0, chart = [];
  608. for (var point in data) {
  609. chart.push([[data[point][1],0]])
  610. i = i + 1;
  611. }
  612. tiny_bar(chart,'#micrograph')
  613. popover_clickedaway = false
  614. popover_visible = true
  615. e.preventDefault()
  616. });
  617. }
  618. function microAnalysisTable (json,field,count) {
  619. var counts = top_field_values(json,field,count)
  620. var table = []
  621. var colors = window.graph_colors;
  622. var i = 0;
  623. $.each(counts, function(index,value){
  624. var show_val = value[0] == '' ? '<i>blank</i>' : xmlEnt(value[0]);
  625. var objids = get_objids_with_field_value(window.resultjson,field,value[0])
  626. var field_val = "<i class=icon-sign-blank style='color:"+colors[i]+"'></i> "+
  627. "<a class='jlink highlight_events' data-mode='value'"+
  628. " data-field='"+field+"' data-objid='"+objids+"'>"+show_val+"</a>";
  629. var buttons = "<span class='raw'>" + xmlEnt(value[0]) + "</span>" +
  630. "<i class='jlink icon-large icon-search msearch'"+
  631. " data-action='' data-field='"+field+"'></i> " +
  632. "<i class='jlink icon-large icon-ban-circle msearch'"+
  633. " data-action='NOT ' data-field='"+field+"'></i> ";
  634. var percent = "<strong>" +
  635. to_percent(value[1],window.resultjson.kibana.per_page) +"</strong>";
  636. table.push([field_val,percent,buttons]);
  637. i = i+1;
  638. });
  639. return CreateTableView(table,
  640. 'table table-condensed table-bordered micro',false,['99%','30px','30px'])
  641. }
  642. function pageLinks() {
  643. // Pagination
  644. var perpage = window.resultjson.kibana.per_page
  645. var str = "<table class='pagelinks'><tr>";
  646. var end = window.hashjson.offset + window.resultjson.hits.hits.length;
  647. if (end < resultjson.hits.total)
  648. {
  649. //str += "<i data-action='nextpage' class='page icon-arrow-right jlink'></i> "
  650. str += "<td width='1%'><a data-action='nextpage' class='page jlink'>Older</a></td>"
  651. }
  652. str += "<td width='99%'><strong>" + window.hashjson.offset + " TO " + end + "</strong></td>";
  653. if (window.hashjson.offset - perpage >= 0) {
  654. //str += "<i data-action='firstpage' " +
  655. // "class='page jlink icon-circle-arrow-left'></i> " +
  656. // "<i data-action='prevpage' class='page icon-arrow-left jlink'></i> ";
  657. str += "<td width='1%'><a data-action='prevpage' class='page jlink'>Newer</a></td> " +
  658. "<td width='1%'> <a data-action='firstpage' class='page jlink'>Newest</a></td>";
  659. }
  660. str += "</tr></table>";
  661. $('.pagelinks').html(str);
  662. }
  663. // This is very ugly
  664. function blank_page() {
  665. var selectors = ['#graph','#graphheader','#feedlinks','#logs','.pagelinks',
  666. '#fields','#analyze']
  667. for (var selector in selectors) {
  668. $(selectors[selector]).text("");
  669. }
  670. }
  671. // This function creates a standard table with column/rows
  672. // objArray = Anytype of object array, like JSON results
  673. // theme (optional) = A css class to add to the table
  674. // enableHeader (optional) = Controls if you want to hide/show, default is show
  675. // widths = control the widths of columns (optional)
  676. function CreateTableView(objArray, theme, enableHeader, widths) {
  677. if (theme === undefined) theme = 'mediumTable'; //default theme
  678. if (enableHeader === undefined) enableHeader = true; //default enable header
  679. // If the returned data is an object do nothing, else try to parse
  680. var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
  681. var str = '<table class="' + theme + '">';
  682. // table head
  683. if (enableHeader) {
  684. str += '<thead><tr>';
  685. for (var index in array[0]) {
  686. str += '<th scope="col">' + field_slim(index) + '</th>';
  687. }
  688. str += '</tr></thead>';
  689. }
  690. // table body
  691. str += '<tbody>';
  692. for (var i = 0; i < array.length; i++) {
  693. str += (i % 2 == 0) ? '<tr class="alt">' : '<tr>';
  694. for (var index in array[i]) {
  695. var width = (!(widths === undefined)) ? 'width="'+widths[index]+'"' : '';
  696. str += '<td '+width+'>' + array[i][index] + '</td>';
  697. }
  698. str += '</tr>';
  699. }
  700. str += '</tbody></table>';
  701. return str;
  702. }
  703. // This function creates a table of LOGS with column/rows
  704. // objArray = Anytype of object array, like JSON results
  705. // fields = Of the fields returned, only display these
  706. // theme (optional) = A css class to add to the table
  707. // enableHeader (optional) = Controls if you want to hide/show, default is show
  708. function CreateLogTable(objArray, fields, theme, enableHeader) {
  709. // set optional theme parameter
  710. theme = theme === undefined ? 'mediumTable' : theme;
  711. enableHeader = enableHeader === undefined ? true : false;
  712. if (objArray === undefined) {
  713. return "<center>" +
  714. "No results match your query. Please try a different search" +
  715. "</center><br>";
  716. }
  717. if (fields.length == 0)
  718. fields = window.resultjson.kibana.default_fields;
  719. // If the returned data is an object do nothing, else try to parse
  720. var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
  721. // Remove empty items from fields array
  722. fields = $.grep(fields, function (n) {
  723. return (n);
  724. });
  725. var str = "<div class='pagelinks'></div>";
  726. str += '<table class="' + theme + '">';
  727. // table head
  728. if (enableHeader) {
  729. str += '<thead><tr>';
  730. str += '<th scope="col" class=firsttd>Time</th>';
  731. for (var index in fields) {
  732. var field = fields[index];
  733. str += '<th scope="col" class="column" data-field="'+field+'">' +
  734. '<i data-mode="left" class="shift_column jlink icon-caret-left"></i>' +
  735. '<div style="display:inline-block;text-align:center">'+field_slim(field) + '</div>'+
  736. ' <i data-mode="right" class="shift_column jlink icon-caret-right"></i></th>';
  737. }
  738. str += '</tr></thead>';
  739. }
  740. // table body
  741. str += '<tbody>';
  742. var i = 1;
  743. for (var objid in array) {
  744. var object = array[objid];
  745. for(var key in object.highlight) {
  746. var hlfield = key;
  747. var hlvalue = object.highlight[hlfield];
  748. }
  749. var id = object._id;
  750. var alt = i % 2 == 0 ? '' : 'alt'
  751. var time = prettyDateString(
  752. Date.parse(get_field_value(object,'@timestamp')) + tOffset);
  753. str += '<tr data-object="' + objid + '" id="logrow_' + objid + '" '+
  754. 'class="' + alt + ' logrow">';
  755. str += '<td class=firsttd>' + time + '</td>';
  756. for (var index in fields) {
  757. var field = fields[index];
  758. if (typeof hlfield === "undefined")
  759. var value = get_field_value(object,field);
  760. else
  761. {
  762. if ( field.toString() == hlfield.toString() )
  763. var value = hlvalue;
  764. else
  765. var value = get_field_value(object,field);
  766. }
  767. var value = value === undefined ? "-" : value.toString();
  768. var value = xmlEnt(wbr(value),10);
  769. var value = value.replace(RegExp("@KIBANA_HIGHLIGHT_START@(.*?)@KIBANA_HIGHLIGHT_END@", "g"),
  770. function (all, text, char) {
  771. return "<span class='highlightedtext'>" + text + "</span>";
  772. }
  773. );
  774. str += '<td class="column" data-field="'+field+'">' +
  775. value + '</td>';
  776. }
  777. str += '</tr><tr class="hidedetails"></tr>';
  778. hlfield = undefined;
  779. i++;
  780. }
  781. str += '</tbody></table>';
  782. str += "<div class='pagelinks'></div>";
  783. return str;
  784. }
  785. // Create a table with details about an object
  786. function details_table(objid,theme) {
  787. if (theme === undefined) theme = 'logdetails table table-bordered';
  788. var obj = window.resultjson.hits.hits[objid];
  789. //obj_fields = get_object_fields(obj);
  790. var obj_fields = flatten_json(obj['_source'])
  791. var str = "<table class='"+theme+"'>" +
  792. "<tr><th>Field</th><th>Action</th><th>Value</th></tr>";
  793. var orig = '';
  794. var i = 1;
  795. for (index in obj_fields) {
  796. var field = index
  797. var value = obj_fields[index];
  798. var field_id = field.replace('@', 'ATSYM');
  799. //var value = get_field_value(obj,field);
  800. var buttons = "<span class='raw'>" + xmlEnt(value) + "</span>" +
  801. "<i class='jlink icon-large icon-search msearch' " +
  802. "data-action='' data-field='"+field+"'></i> " +
  803. "<i class='jlink icon-large icon-ban-circle msearch' " +
  804. "data-action='NOT ' data-field='"+field+"'></i> ";
  805. var trclass = (i % 2 == 0) ?
  806. 'class="alt '+field_id+'_row"' : 'class="'+field_id+'_row"';
  807. // Are URLs clickable ?
  808. if (resultjson.kibana.clickable_urls) {
  809. var value = value === undefined ? "-" : value.toString();
  810. // Detect URLs and inserts delimiters
  811. var value = value.replace(RegExp("(https?://([-\\w\\.]+)+(:\\d+)?(/([-\\w/_\\.]*(\\?\\S+)?)?)?)", "g"),
  812. function (all, text, char) {
  813. return "@KIBANA_LINK_START@" + text + "@KIBANA_LINK_END@";
  814. }
  815. );
  816. var value = xmlEnt(wbr(value),10);
  817. // Replace delimiters by HTML code
  818. var value = value.replace(RegExp("@KIBANA_LINK_START@(.*?)@KIBANA_LINK_END@", "g"),
  819. function (all, text) {
  820. // Clean link
  821. var stripped = text.replace( new RegExp("<del>&#8203;</del>","g"),"");
  822. return "<a href='" + stripped + "' target='_blank'>" + text + "</a>";
  823. }
  824. );
  825. str += "<tr " + trclass + ">" +
  826. "<td class='firsttd " + field_id + "_field'>" + field + "</td>" +
  827. "<td style='width: 60px'>" + buttons + "</td>" +
  828. '<td>' + value +
  829. "</td></tr>";
  830. } else {
  831. str += "<tr " + trclass + ">" +
  832. "<td class='firsttd " + field_id + "_field'>" + field + "</td>" +
  833. "<td style='width: 60px'>" + buttons + "</td>" +
  834. '<td>' + xmlEnt(wbr(value, 10)) +
  835. "</td></tr>";
  836. }
  837. i++;
  838. }
  839. str += "</table>";
  840. return str;
  841. }
  842. function mSearch(field, value, mode) {
  843. window.hashjson.offset = 0;
  844. if (mode === undefined) mode = '';
  845. if (mode != 'analysis') {
  846. window.hashjson.mode = mode;
  847. window.hashjson.analyze_field = '';
  848. }
  849. var pattern=/^(.*)\|([^"']*)$/;
  850. var queryinput=$('#queryinput').val();
  851. if (pattern.test(queryinput) == true) {
  852. var results = queryinput.match(pattern);
  853. var queryinput = $.trim(results[1]);
  854. var fields = $.trim(results[2]).split(/\s+/).slice(1);
  855. var values = value.toString().split('||');
  856. var query = '';
  857. var glue = ''
  858. // TODO: This only works if a query already exists I think?
  859. for (var count = 0;count < fields.length;count++) {
  860. value = values[count];
  861. if (value == "null" || typeof value === "undefined") {
  862. value = fields[count];
  863. field = '_missing_'
  864. } else {
  865. field = fields[count];
  866. }
  867. query = query + glue + field + ":" + "\"" + addslashes(value.toString()) + "\"";
  868. glue = " AND ";
  869. }
  870. } else {
  871. var query = field + ":" + "\"" + addslashes(value.toString()) + "\"";
  872. }
  873. var glue = queryinput != "" ? " AND " : " ";
  874. window.hashjson.search = queryinput + glue + query;
  875. setHash(window.hashjson);
  876. scroll(0, 0);
  877. }
  878. function field_alias(field) {
  879. return field.replace('@', 'ATSYM');
  880. }
  881. function field_slim(field) {
  882. return field.replace(/(.*)\.(.*)/,"<span class=small>$1.</span><br>$2");
  883. }
  884. function mFields(field) {
  885. // If the field is not in the hashjson, add it
  886. if ($.inArray(field, window.hashjson.fields) < 0) {
  887. // We're adding a field, but there's nothing in the hashjson,
  888. // remove default fields
  889. if (window.hashjson.fields.length == 0) {
  890. $('#logs').find('tr.logrow').each(function(){
  891. $('.column').remove();
  892. });
  893. }
  894. // Add field to hashjson
  895. window.hashjson.fields.push(field);
  896. $('#fields ul.unselected a[data-field="' + field + '"]').addClass('selected');
  897. $('#fields ul.unselected i[data-field="' + field + '"]').removeClass('icon-plus');
  898. $('#fields ul.unselected i[data-field="' + field + '"]').addClass('icon-minus');
  899. // Add column
  900. $('#logs').find('tr.logrow').each(function(){
  901. var obj = window.resultjson.hits.hits[$(this).attr('data-object')];
  902. var value = get_field_value(obj,field)
  903. $(this).find('td').last().after(
  904. '<td class="column" data-field="'+field+'">' + xmlEnt(wbr(value, 10)) + '</td>');
  905. });
  906. $('#logs thead tr').find('th').last().after(
  907. '<th scope="col" class="column" data-field="'+field+'">' +
  908. '<i data-mode="left" class="shift_column jlink icon-caret-left"></i>' +
  909. '<div style="display:inline-block;text-align:center">'+field_slim(field) + '</div>'+
  910. ' <i data-mode="right" class="shift_column jlink icon-caret-right"></i></th>'
  911. );
  912. } else {
  913. $('#logs .column[data-field="'+field+'"]').remove();
  914. // Otherwise, remove it
  915. window.hashjson.fields = jQuery.grep(
  916. window.hashjson.fields, function (value) {
  917. return value != field;
  918. }
  919. );
  920. // Remove from selected, add to unselected
  921. $('#fields ul.unselected a[data-field="' + field + '"]').removeClass('selected');
  922. $('#fields ul.unselected i[data-field="' + field + '"]').removeClass('icon-minus');
  923. $('#fields ul.unselected i[data-field="' + field + '"]').addClass('icon-plus');
  924. if (window.hashjson.fields.length == 0) {
  925. $.each(window.resultjson.kibana.default_fields, function(index,field){
  926. $('#logs').find('tr.logrow').each(function(){
  927. var obj = window.resultjson.hits.hits[$(this).attr('data-object')];
  928. var value = get_field_value(obj,field)
  929. $(this).find('td').last().after(
  930. '<td class="column" data-field="'+field+'">' +
  931. xmlEnt(wbr(value, 10)) + '</td>');
  932. });
  933. $('#logs thead tr').find('th').last().after(
  934. '<th scope="col" class="column" data-field="'+field+'">' +
  935. field_slim(field) + '</th>');
  936. });
  937. }
  938. }
  939. // Remove empty items if they exist
  940. window.hashjson.fields = $.grep(window.hashjson.fields,function(n){
  941. return(n);
  942. });
  943. $('#feedlinks').html(feedLinks(window.hashjson));
  944. enable_popovers();
  945. pageLinks();
  946. }
  947. function feedLinks(obj) {
  948. return "<a href=rss/" + Base64.encode(JSON.stringify(obj, null, '')) +">rss " +
  949. "<i class='icon-rss'></i></a> "+
  950. "<a href=export/" + Base64.encode(JSON.stringify(obj, null, '')) + ">export " +
  951. "<i class='icon-hdd'></i></a> "+
  952. "<a href=stream#" + Base64.encode(JSON.stringify(obj, null, '')) + ">stream " +
  953. "<i class='icon-dashboard'></i></a>"
  954. }
  955. $(function () {
  956. $('form').submit(function () {
  957. if (window.hashjson.search != $('#queryinput').val()) {
  958. window.hashjson.offset = 0;
  959. window.hashjson.search = $('#queryinput').val();
  960. }
  961. window.hashjson.stamp = new Date().getTime();
  962. if (window.hashjson.timeframe == "custom")
  963. $('#timechange').click();
  964. else
  965. window.hashjson.timeframe = $('#timeinput').val();
  966. var pattern=/^(.*)\|([^"']*)$/;
  967. if (pattern.test(window.hashjson.search) == true) {
  968. var results = window.hashjson.search.match(pattern);
  969. var search = $.trim(results[1]);
  970. var fields = $.trim(results[2]).split(/\s+/);
  971. var field = fields.slice(1).join(',,');
  972. var mode = fields[0];
  973. window.hashjson.mode = mode;
  974. if (mode == 'columns')
  975. window.hashjson.fields = field.split(',,')
  976. else
  977. window.hashjson.analyze_field = field;
  978. }
  979. if (window.location.hash == "#" + JSON.stringify(window.hashjson, null, ''))
  980. pageload(window.location.hash);
  981. else
  982. setHash(window.hashjson);
  983. return false;
  984. });
  985. });
  986. function datepickers(from,to) {
  987. var graph_interval = window.hashjson.time.user_interval;
  988. var interval_opts = {
  989. auto:0,
  990. second:1000,
  991. minute:60*1000,
  992. hour:60*60*1000,
  993. day:60*60*24*1000
  994. };
  995. var options = ''
  996. $.each(interval_opts,function(i,interval) {
  997. options += '<option value='+interval+(interval == graph_interval ? ' selected' : '') +
  998. '>'+i+'</option>';
  999. });
  1000. $('#graphheader').html("<div class='form-inline'>"+
  1001. "<input size=19 id=timefrom class='datetimeRange'" +
  1002. " type=text name=timefrom> to " +
  1003. "<input size=19 id=timeto class='datetimeRange'" +
  1004. " type=text name=timeto> grouped by " +
  1005. "<select id=user_interval name=user_interval> " + options + "</select>" +
  1006. "<button id='timechange' class='btn btn-small jlink' " +
  1007. "style='visibility: hidden'> filter</button></div>");
  1008. var from_date = utc_date_obj(new Date(from - tOffset))
  1009. var to_date = utc_date_obj(new Date(to - tOffset));
  1010. $('#timefrom').datetimeEntry({
  1011. maxDatetime : to_date,
  1012. datetimeFormat: 'Y-O-D H:M:S',
  1013. spinnerImage: ''
  1014. });
  1015. $('#timefrom').datetimeEntry('setDatetime',from_date)
  1016. $('#timeto').datetimeEntry({
  1017. minDatetime: $('#timefrom').datetimeEntry('getDatetime'),
  1018. maxDatetime: utc_date_obj(new Date()),
  1019. datetimeFormat: 'Y-O-D H:M:S',
  1020. spinnerImage: ''
  1021. },to);
  1022. $('#timeto').datetimeEntry('setDatetime',to_date)
  1023. // LOL Wat? o_from and o_to are globals?! I should be beaten with a hose for
  1024. // the horrid way time is handled in this application. Stupid FLOT.
  1025. $('#timefrom,#timeto').datepicker({
  1026. format: 'yyyy-mm-dd'
  1027. }).on('show', function(ev) {
  1028. o_from = local_date_obj(
  1029. new Date($('#timefrom').datetimeEntry('getDatetime').getTime() - tOffset));
  1030. o_to = local_date_obj(
  1031. new Date($('#timeto').datetimeEntry('getDatetime').getTime() - tOffset));
  1032. });
  1033. }
  1034. // Render the date/time picker
  1035. // Must make this pretty
  1036. function renderDateTimePicker(from, to, force) {
  1037. $('.datepicker').remove()
  1038. from = from + tOffset
  1039. to = to + tOffset
  1040. if (!$('#timechange').length || force == true) {
  1041. datepickers(from,to)
  1042. $('#timefrom').datepicker().on('changeDate', function(ev) {
  1043. o_from.setUTCFullYear(ev.date.getFullYear())
  1044. o_from.setUTCMonth(ev.date.getMonth())
  1045. o_from.setUTCDate(ev.date.getDate())
  1046. $('.datepicker').remove()
  1047. renderDateTimePicker(
  1048. o_from.getTime() + tOffset,
  1049. o_to.getTime() + tOffset,
  1050. true
  1051. );
  1052. window.hashjson.timeframe = 'custom'
  1053. $('#timeinput').val('custom');
  1054. })
  1055. $('#timeto').datepicker().on('changeDate', function(ev) {
  1056. o_to.setUTCFullYear(ev.date.getFullYear())
  1057. o_to.setUTCMonth(ev.date.getMonth())
  1058. o_to.setUTCDate(ev.date.getDate())
  1059. $('.datepicker').remove()
  1060. renderDateTimePicker(
  1061. o_from.getTime() + tOffset,
  1062. o_to.getTime() + tOffset,
  1063. true
  1064. );
  1065. window.hashjson.timeframe = 'custom'
  1066. $('#timeinput').val('custom');
  1067. })
  1068. $('input.datetimeRange').datetimeEntry({datetimeFormat: 'Y-O-D H:M:S'}).
  1069. change(function() {
  1070. $('#' + (this.id == 'timefrom' ? 'timeto' : 'timefrom')).datetimeEntry(
  1071. 'change', (this.id == 'timefrom' ? 'minDatetime' : 'maxDatetime'),
  1072. $(this).datetimeEntry('getDatetime'));
  1073. });
  1074. $('#user_interval').change(function () {
  1075. var interval = $('#user_interval').val()
  1076. if(typeof window.hashjson.time == 'undefined')
  1077. window.hashjson.time = {user_interval:interval};
  1078. else
  1079. window.hashjson.time.user_interval = interval;
  1080. });
  1081. $('#timefrom,#timeto').change(function () {
  1082. var time = {
  1083. "from": field_time('#timefrom'),
  1084. "to": field_time('#timeto'),
  1085. "user_interval": $('#user_interval').val()
  1086. };
  1087. window.hashjson.offset = 0;
  1088. window.hashjson.time = time;
  1089. $('#timeinput').val('custom');
  1090. $('#timeinput').change();
  1091. });
  1092. // Give user a nice interface for selecting time ranges
  1093. $("#timechange").click(function () {
  1094. var time = {
  1095. "from": field_time('#timefrom'),
  1096. "to": field_time('#timeto'),
  1097. "user_interval": $('#user_interval').val()
  1098. };
  1099. window.hashjson.offset = 0;
  1100. window.hashjson.time = time;
  1101. window.hashjson.timeframe = "custom";
  1102. setHash(window.hashjson);
  1103. });
  1104. }
  1105. }
  1106. function field_time(selector) {
  1107. var tz_offset = int_to_tz(window.tOffset);
  1108. var str = ISODateString(
  1109. new Date($(selector).datetimeEntry('getDatetime').getTime())
  1110. ) + tz_offset;
  1111. return str;
  1112. }
  1113. function tiny_bar(data,selector) {
  1114. $.plot( $(selector),data, {
  1115. series: {
  1116. stack: true,
  1117. lines: { show:false },
  1118. bars: { show: true, horizontal:true, fill: 1 }
  1119. },
  1120. xaxis: {show:false, max: window.resultjson.kibana.per_page},
  1121. yaxis: {show:false},
  1122. grid: {show:false},
  1123. });
  1124. }
  1125. // Big horrible function for creating graphs
  1126. function logGraph(data, interval, metric) {
  1127. // If mode is graph, graph count, otherwise remove word 'graph' and chart
  1128. // whatever is left. ie meangraph -> mean
  1129. if (typeof metric === 'undefined')
  1130. metric = 'count';
  1131. metric = metric.replace('graph','');
  1132. if (metric === '')
  1133. metric = 'count';
  1134. var array = new Array();
  1135. var from, to;
  1136. if(typeof window.resultjson.kibana.time !== 'undefined') {
  1137. // add null value at time from.
  1138. if(window.hashjson.timeframe != 'all') {
  1139. from = Date.parse(window.resultjson.kibana.time.from) + tOffset
  1140. array.push(
  1141. Array(from, null));
  1142. }
  1143. }
  1144. for (var index in data) {
  1145. var value = data[index][metric];
  1146. array.push(Array(data[index].time + tOffset, value));
  1147. }
  1148. if(typeof window.resultjson.kibana.time !== 'undefined') {
  1149. // add null value at time to.
  1150. to = Date.parse(window.resultjson.kibana.time.to) + tOffset
  1151. array.push(
  1152. Array(to, null));
  1153. }
  1154. from = array[0][0];
  1155. to = array[array.length -1][0]
  1156. renderDateTimePicker(from,to,true);
  1157. // Make sure we get results before calculating graph stuff
  1158. if (!jQuery.isEmptyObject(data)) {
  1159. // Allow user to select ranges on graph.
  1160. // Its this OR click, not both it seems.
  1161. var intset = false;
  1162. $('#graph').bind("plotselected", function (event, ranges) {
  1163. if (!intset) {
  1164. intset = true;
  1165. var from = utc_date_obj(new Date(parseInt(ranges.xaxis.from.toFixed(0))))
  1166. var to = utc_date_obj(new Date(parseInt(ranges.xaxis.to.toFixed(0))))
  1167. var time = {
  1168. "from": ISODateString(from)+int_to_tz(window.tOffset),
  1169. "to": ISODateString(to)+int_to_tz(window.tOffset)
  1170. };
  1171. window.hashjson.offset = 0;
  1172. window.hashjson.time = time;
  1173. window.hashjson.timeframe = "custom";
  1174. setHash(window.hashjson);
  1175. }
  1176. });
  1177. // Allow user to hover over a bar and get details
  1178. var previousPoint = null;
  1179. $("#graph").bind("plothover", function (event, pos, item) {
  1180. $("#x").text(pos.x.toFixed(2));
  1181. $("#y").text(pos.y.toFixed(2));
  1182. if (item) {
  1183. if (previousPoint != item.dataIndex) {
  1184. previousPoint = item.dataIndex;
  1185. $("#tooltip").remove();
  1186. var x = item.datapoint[0].toFixed(0),
  1187. y = Math.round(item.datapoint[1]*100)/100;
  1188. showTooltip(
  1189. item.pageX-120, item.pageY-20, y + " at " + prettyDateString(x)
  1190. );
  1191. }
  1192. } else {
  1193. $("#tooltip").remove();
  1194. previousPoint = null;
  1195. }
  1196. });
  1197. var label = 'Logs';
  1198. if (metric !== '')
  1199. label = metric;
  1200. var color = getGraphColor(metric);
  1201. $.plot(
  1202. $("#graph"), [
  1203. {
  1204. data: array,
  1205. label: label + " per " + secondsToHms(parseInt(interval) / 1000)
  1206. }
  1207. ], {
  1208. legend: { position: "nw" },
  1209. series: {
  1210. lines: { show: false, fill: true },
  1211. bars: { show: true, fill: 1, barWidth: interval / 1.7 },
  1212. points: { show: false },
  1213. color: color,
  1214. shadowSize: 1
  1215. },
  1216. xaxis: {
  1217. mode: "time",
  1218. timeformat: "%H:%M:%S<br>%m-%d",
  1219. label: "Datetime",
  1220. color: "#000",
  1221. },
  1222. yaxis: {
  1223. min: 0,
  1224. color: "#000"
  1225. },
  1226. selection: {
  1227. mode: "x",
  1228. color: '#000'
  1229. },
  1230. grid: {
  1231. backgroundColor: '#fff',
  1232. borderWidth: 0,
  1233. borderColor: '#000',
  1234. color: "#ddd",
  1235. hoverable: true,
  1236. clickable: true
  1237. }
  1238. });
  1239. }
  1240. $("#graph_container").resizable({
  1241. minHeight: 100,
  1242. handles: 's',
  1243. stop: function(event, ui) {
  1244. $("#graph_container").css('width', '');
  1245. }
  1246. });
  1247. }
  1248. function showTooltip(x, y, contents) {
  1249. $('<div id="tooltip">' + contents + '</div>').css({
  1250. position: 'absolute',
  1251. display: 'none',
  1252. top: y,
  1253. left: x,
  1254. color: '#eee',
  1255. border: '1px solid #fff',
  1256. padding: '3px',
  1257. 'font-size': '8pt',
  1258. 'background-color': '#000',
  1259. border: '1px solid #000',
  1260. 'font-family': '"Verdana", Geneva, sans-serif'
  1261. }).appendTo("body").fadeIn(200);
  1262. }
  1263. function sbctl(mode,user_selected) {
  1264. var sb = $('#sidebar'),
  1265. main = $('#main'),
  1266. lnk = $('#sbctl'),
  1267. win = $(window);
  1268. if(user_selected) {
  1269. window.sb = mode;
  1270. }
  1271. if (mode == 'hide') {
  1272. // collapse
  1273. sb.hide();
  1274. main.removeClass('span10');
  1275. main.addClass('span12');
  1276. lnk.removeClass('ui-icon-triangle-1-w');
  1277. lnk.addClass('ui-icon-triangle-1-e');
  1278. main.addClass('sidebar-collapsed');
  1279. win.smartresize();
  1280. }
  1281. if (mode == 'show') {
  1282. sb.show();
  1283. main.removeClass('span12');
  1284. main.addClass('span10');
  1285. lnk.removeClass('ui-icon-triangle-1-e');
  1286. lnk.addClass('ui-icon-triangle-1-w');
  1287. main.removeClass('sidebar-collapsed');
  1288. win.smartresize();
  1289. }
  1290. }
  1291. function move_column(field,dir) {
  1292. var x = dir == 'right' ? 1 : -2
  1293. var len = $('#logs thead th').length
  1294. if (len == 2)
  1295. return
  1296. var thi = $('#logs th.column[data-field="'+field+'"]').index()
  1297. dest = thi+x
  1298. if (x == -2 && thi == 1)
  1299. dest = len-1
  1300. if (x == 1 && thi == (len-1))
  1301. dest = 0
  1302. var th1 = $('#logs thead th:eq('+thi+')')
  1303. var th2 = $('#logs thead th:eq('+dest+')')
  1304. th1.detach().insertAfter(th2)
  1305. $('#logs tr.logrow').each(function() {
  1306. var tr = $(this);
  1307. var td1 = tr.find('td:eq('+thi+')');
  1308. var td2 = tr.find('td:eq('+dest+')');
  1309. td1.detach().insertAfter(td2);
  1310. });
  1311. window.hashjson.fields = $('#logs th.column').map(
  1312. function(){return $(this).attr("data-field");}
  1313. ).get()
  1314. }
  1315. function showError(title,text) {
  1316. blank_page();
  1317. $('#logs').html("<h2>"+title+"</h2>"+text);
  1318. // We have to use hashjson's time here since we won't
  1319. // get a resultjson on error, usually
  1320. if(
  1321. typeof window.hashjson.time.from !== 'undefined' &&
  1322. typeof window.hashjson.time.to !== 'undefined'
  1323. ) {
  1324. renderDateTimePicker(
  1325. Date.parse(window.hashjson.time.from)+tOffset,
  1326. Date.parse(window.hashjson.time.to)+tOffset
  1327. );
  1328. }
  1329. sbctl('hide')
  1330. }
  1331. function getGraphColor(mode) {
  1332. switch(mode)
  1333. {
  1334. case "mean":
  1335. var color = '#ef9a23';
  1336. break;
  1337. default:
  1338. var color = '#5aba65';
  1339. }
  1340. return color;
  1341. }
  1342. function resetAll() {
  1343. window.hashjson = JSON.parse(
  1344. '{'+
  1345. '"search":"",'+
  1346. '"fieā€¦

Large files files are truncated, but you can click here to view the full file