diff --git a/CHANGELOG b/CHANGELOG index e91794e192..50a1ce9515 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,8 @@ - connections.csv returns session count (issue #356) - Expose the id/rootId in SPI Meta (issue #355) - WISE - right click loading from files + - spiview improvements - collapsible sections, sorting, + right click to set load on default (issue #360) 0.11.3 2015/02/26 diff --git a/NOTICE b/NOTICE index 515b6feb0e..e1c58302fc 100644 --- a/NOTICE +++ b/NOTICE @@ -145,6 +145,61 @@ Redistribution and use in source and binary forms, with or without modification, THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +================================================================================ +viewer/public/jquery.collapsible.js - BSD - https://github.com/juven14/Collapsible +viewer/public/plus.png +viewer/public/minus.png + +New BSD License + +Collapsible, jQuery Plugin +Copyright (c) 2010, John Snyder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Collapsible, jQuery Plugin nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL JOHN SNYDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +================================================================================ +viewer/public/jquery.cookie.js - MIT - https://github.com/carhartl/jquery-cookie + +Copyright 2014 Klaus Hartl + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================================================ viewer/public/flot - MIT - http://code.google.com/p/flot/ @@ -171,7 +226,6 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ================================================================================ viewer/public/highlight.min.js - BSD - http://code.google.com/p/testprogramming/source/browse/trunk/javascript/svg @@ -230,7 +284,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ================================================================================ -viewer/public/jquery-jvectormap-1.2.2.min.js - MIT - http://jvectormap.com/ +viewer/public/jquery-jvectormap* - MIT - http://jvectormap.com/ Copyright 2011-2012, Kirill Lebedev Licensed under the MIT license. diff --git a/viewer/public/jquery.collapsible.js b/viewer/public/jquery.collapsible.js new file mode 100644 index 0000000000..e30abbc8fb --- /dev/null +++ b/viewer/public/jquery.collapsible.js @@ -0,0 +1,492 @@ +/** + * Collapsible, jQuery Plugin + * + * Collapsible management. Optional cookie support using the jQuery Cookie plugin: + * https://github.com/carhartl/jquery-cookie + * + * Copyright (c) 2010 John Snyder (snyderplace.com) + * @license http://www.snyderplace.com/collapsible/license.txt New BSD + * @version 1.2.1 + */ +(function($) { + $.fn.collapsible = function (cmd, arg) { + + //firewalling + if (!this || this.length < 1) { + return this; + } + + //address command requests + if (typeof cmd == 'string') { + return $.fn.collapsible.dispatcher[cmd](this, arg); + } + + //return the command dispatcher + return $.fn.collapsible.dispatcher['_create'](this, cmd); + }; + + //create the command dispatcher + $.fn.collapsible.dispatcher = { + + //initialized with options + _create : function(obj, arg) { + createCollapsible(obj, arg); + }, + + //toggle the element's display + toggle: function(obj) { + toggle(obj, loadOpts(obj)); + return obj; + }, + + //show the element + open: function(obj) { + open(obj, loadOpts(obj)); + return obj; + }, + + //hide the element + close: function(obj) { + close(obj, loadOpts(obj)); + return obj; + }, + + //check if the element is closed + collapsed: function(obj) { + return collapsed(obj, loadOpts(obj)); + }, + + //open all closed containers + openAll: function(obj) { + return openAll(obj, loadOpts(obj)); + }, + + //close all opened containers + closeAll: function(obj) { + return closeAll(obj, loadOpts(obj)); + } + }; + + //create the initial collapsible + function createCollapsible(obj, options) + { + + //build main options before element iteration + var opts = $.extend({}, $.fn.collapsible.defaults, options); + + //store any opened default values to set cookie later + var opened = []; + + //iterate each matched object, bind, and open/close + obj.each(function() { + + var $this = $(this); + saveOpts($this, opts); + + //bind it to the event + if (opts.bind == 'mouseenter') { + + $this.bind('mouseenter', function(e) { + e.preventDefault(); + toggle($this, opts); + }); + } + + //bind it to the event + if (opts.bind == 'mouseover') { + + $this.bind('mouseover',function(e) { + e.preventDefault(); + toggle($this, opts); + }); + } + + //bind it to the event + if (opts.bind == 'click') { + + $this.bind('click', function(e) { + e.preventDefault(); + toggle($this, opts); + }); + + } + + //bind it to the event + if (opts.bind == 'dblclick') { + + $this.bind('dblclick', function(e) { + + e.preventDefault(); + toggle($this, opts); + }); + + } + + //initialize the collapsibles + //get the id for this element + var id = $this.attr('id'); + + //if not using cookies, open defaults + if (!useCookies(opts)) { + + //is this collapsible in the default open array? + var dOpenIndex = inDefaultOpen(id, opts); + + //close it if not defaulted to open + if (dOpenIndex === false) { + + $this.addClass(opts.cssClose); + opts.loadClose($this, opts); + + } else { //its a default open, open it + + $this.addClass(opts.cssOpen); + opts.loadOpen($this, opts); + opened.push(id); + } + + } else { //can use cookies, use them now + + //has a cookie been set, this overrides default open + if (issetCookie(opts)) { + + var cookieIndex = inCookie(id, opts); + + if (cookieIndex === false) { + + $this.addClass(opts.cssClose); + opts.loadClose($this, opts); + + } else { + + $this.addClass(opts.cssOpen); + opts.loadOpen($this, opts); + opened.push(id); + } + + } else { //a cookie hasn't been set open defaults, add them to opened array + + dOpenIndex = inDefaultOpen(id, opts); + + if (dOpenIndex === false) { + + $this.addClass(opts.cssClose); + opts.loadClose($this, opts); + + } else { + + $this.addClass(opts.cssOpen); + opts.loadOpen($this, opts); + opened.push(id); + } + } + } + }); + + //now that the loop is done, set the cookie + if (opened.length > 0 && useCookies(opts)) { + + setCookie(opened.toString(), opts); + + } else { //there are none open, set cookie + + setCookie('', opts); + } + + return obj; + } + + //load opts from object + function loadOpts($this) { + return $this.data('collapsible-opts'); + } + + //save opts into object + function saveOpts($this, opts) { + return $this.data('collapsible-opts', opts); + } + + //returns true if object is opened + function collapsed($this, opts) { + return $this.hasClass(opts.cssClose); + } + + //hides a collapsible + function close($this, opts) { + + //give the proper class to the linked element + $this.addClass(opts.cssClose).removeClass(opts.cssOpen); + + //close the element + opts.animateClose($this, opts); + + //do cookies if plugin available + if (useCookies(opts)) { + // split the cookieOpen string by "," + var id = $this.attr('id'); + unsetCookieId(id, opts); + } + } + + //opens a collapsible + function open($this, opts) { + + //give the proper class to the linked element + $this.removeClass(opts.cssClose).addClass(opts.cssOpen); + + //open the element + opts.animateOpen($this, opts); + + //do cookies if plugin available + if (useCookies(opts)) { + + // split the cookieOpen string by "," + var id = $this.attr('id'); + appendCookie(id, opts); + } + } + + //toggle a collapsible on an event + function toggle($this, opts) { + + if (collapsed($this, opts)) { + + //open a closed element + open($this, opts); + + } else { + + //close an open element + close($this, opts); + } + + return false; + } + + //open all closed containers + function openAll($this, opts) { + + // loop through all container elements + $.each($this, function(elem, value) { + + if (collapsed($(value), opts)) { + + //open a closed element + open($(value), opts); + } + }); + } + + //close all open containers + function closeAll($this, opts) { + + $.each($this, function(elem, value) { + + if (!collapsed($(value), opts)) { + + //close an opened element + close($(value), opts); + } + }); + } + + //use cookies? + function useCookies(opts) { + + //return false if cookie plugin not present or if a cookie name is not provided + if (!$.cookie || opts.cookieName == '') { + return false; + } + + //we can use cookies + return true; + } + + //append a collapsible to the cookie + function appendCookie(value, opts) { + + //check if cookie plugin available and cookiename is set + if (!useCookies(opts)) { + return false; + } + + //does a cookie already exist + if (!issetCookie(opts)) { + + //no lets set one + setCookie(value, opts); + return true; + } + + //cookie already exists, is this collapsible already set? + if (inCookie(value, opts)) { //yes, quit here + return true; + } + + //get the cookie + var cookie = decodeURIComponent($.cookie(opts.cookieName)); + + //turn it into an array + var cookieArray = cookie.split(','); + + //add it to list + cookieArray.push(value); + + //save it + setCookie(cookieArray.toString(), opts); + + return true; + } + + //unset a collapsible from the cookie + function unsetCookieId(value, opts) + { + //check if cookie plugin available and cookiename is set + if (!useCookies(opts)) { + return false; + } + + //if its not there we don't need to remove from it + if (!issetCookie(opts)) { //quit here, don't have a cookie + return true; + } + + //we have a cookie, is this collapsible in it + var cookieIndex = inCookie(value, opts); + if (cookieIndex === false) { //not in the cookie quit here + return true; + } + + //still here get the cookie + var cookie = decodeURIComponent($.cookie(opts.cookieName)); + + //turn it into an array + var cookieArray = cookie.split(','); + + //lets pop it out of the array + cookieArray.splice(cookieIndex, 1); + + //overwrite + setCookie(cookieArray.toString(), opts); + + return true + } + + //set a cookie + function setCookie(value, opts) + { + //can use the cookie plugin + if (!useCookies(opts)) { //no, quit here + return false; + } + + //cookie plugin is available, lets set the cookie + $.cookie(opts.cookieName, value, opts.cookieOptions); + + return true; + } + + //check if a collapsible is in the cookie + function inCookie(value, opts) + { + //can use the cookie plugin + if (!useCookies(opts)) { + return false; + } + + //if its not there we don't need to remove from it + if (!issetCookie(opts)) { //quit here, don't have a cookie + return false; + } + + //get the cookie value + var cookie = decodeURIComponent($.cookie(opts.cookieName)); + + //turn it into an array + var cookieArray = cookie.split(','); + + //get the index of the collapsible if in the cookie array + var cookieIndex = $.inArray(value, cookieArray); + + //is this value in the cookie array + if (cookieIndex == -1) { //no, quit here + return false; + } + + return cookieIndex; + } + + //check if a cookie is set + function issetCookie(opts) + { + //can we use the cookie plugin + if (!useCookies(opts)) { //no, quit here + return false; + } + + //is the cookie set + if ($.cookie(opts.cookieName) === null) { //no, quit here + return false; + } + + return true; + } + + //check if a collapsible is in the list of collapsibles to be opened by default + function inDefaultOpen(id, opts) + { + //get the array of open collapsibles + var defaultOpen = getDefaultOpen(opts); + + //is it in the default open array + var index = $.inArray(id, defaultOpen); + if (index == -1) { //nope, quit here + return false; + } + + return index; + } + + //get the default open collapsibles and return array + function getDefaultOpen(opts) + { + //initialize an empty array + var defaultOpen = []; + + //if there is a list, lets split it into an array + if (opts.defaultOpen != '') { + defaultOpen = opts.defaultOpen.split(','); + } + + return defaultOpen; + } + + // settings + $.fn.collapsible.defaults = { + cssClose: 'collapse-close', //class you want to assign to a closed collapsible header + cssOpen: 'collapse-open', //class you want to assign an opened collapsible header + cookieName: 'collapsible', //name of the cookie you want to set for this collapsible + cookieOptions: { //cookie options, see cookie plugin for details + path: '/', + expires: 7, + domain: '', + secure: '' + }, + defaultOpen: '', //comma separated list of header ids that you want opened by default + speed: 'slow', //speed of the slide effect + bind: 'click', //event to bind to, supports click, dblclick, mouseover and mouseenter + animateOpen: function (elem, opts) { //replace the standard slideUp with custom function + elem.next().stop(true, true).slideDown(opts.speed); + }, + animateClose: function (elem, opts) { //replace the standard slideDown with custom function + elem.next().stop(true, true).slideUp(opts.speed); + }, + loadOpen: function (elem, opts) { //replace the default open state with custom function + elem.next().show(); + }, + loadClose: function (elem, opts) { //replace the default close state with custom function + elem.next().hide(); + } + }; + +})(jQuery); \ No newline at end of file diff --git a/viewer/public/jquery.cookie.js b/viewer/public/jquery.cookie.js new file mode 100644 index 0000000000..96e9735ab9 --- /dev/null +++ b/viewer/public/jquery.cookie.js @@ -0,0 +1,95 @@ +/*! + * jQuery Cookie Plugin v1.3.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery'], factory); + } else { + // Browser globals. + factory(jQuery); + } +}(function ($) { + + var pluses = /\+/g; + + function raw(s) { + return s; + } + + function decoded(s) { + return decodeURIComponent(s.replace(pluses, ' ')); + } + + function converted(s) { + if (s.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape + s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + try { + return config.json ? JSON.parse(s) : s; + } catch(er) {} + } + + var config = $.cookie = function (key, value, options) { + + // write + if (value !== undefined) { + options = $.extend({}, config.defaults, options); + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = config.json ? JSON.stringify(value) : String(value); + + return (document.cookie = [ + config.raw ? key : encodeURIComponent(key), + '=', + config.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // read + var decode = config.raw ? raw : decoded; + var cookies = document.cookie.split('; '); + var result = key ? undefined : {}; + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = decode(parts.join('=')); + + if (key && key === name) { + result = converted(cookie); + break; + } + + if (!key) { + result[name] = converted(cookie); + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) !== undefined) { + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, { expires: -1 })); + return true; + } + return false; + }; + +})); \ No newline at end of file diff --git a/viewer/viewer.js b/viewer/viewer.js index 9721a3cfd3..db49ccf767 100644 --- a/viewer/viewer.js +++ b/viewer/viewer.js @@ -257,7 +257,7 @@ function loadPlugins() { }, getDb: function() { return Db; }, getPcap: function() { return Pcap; }, - } + }; var plugins = Config.get("viewerPlugins", "").split(";"); var dirs = Config.get("pluginsDir", "/data/moloch/plugins").split(";"); plugins.forEach(function (plugin) { @@ -584,10 +584,16 @@ function checkToken(req, res, next) { } req.token = Config.auth2obj(req.body.token); - if (Math.abs(Date.now() - req.token.date) > 600000 || req.token.pid !== process.pid || req.token.userId !== req.user.userId) { + var diff = Math.abs(Date.now() - req.token.date); + if (diff > 2400000 || req.token.pid !== process.pid || req.token.userId !== req.user.userId) { console.trace("bad token", req.token); return res.send(JSON.stringify({success: false, text: "Timeout - Please try reloading page and repeating the action"})); } + // Shorter token timeout if editing someone elses info + if (req.token.suserId && req.token.userId !== req.user.userId && diff > 600000) { + console.trace("admin bad token", req.token); + return res.send(JSON.stringify({success: false, text: "Admin Timeout - Please try reloading page and repeating the action"})); + } return next(); } @@ -630,7 +636,8 @@ app.get("/spiview", checkWebEnabled, function(req, res) { reqFields: Config.headers("headers-http-request"), resFields: Config.headers("headers-http-response"), emailFields: Config.headers("headers-email"), - categories: Config.getCategories() + categories: Config.getCategories(), + token: Config.obj2auth({date: Date.now(), pid: process.pid, userId: req.user.userId, suserId: req.user.userId}) }); }); diff --git a/viewer/views/spiview.jade b/viewer/views/spiview.jade index bc305a4ae8..63b67b6a4d 100644 --- a/viewer/views/spiview.jade +++ b/viewer/views/spiview.jade @@ -7,18 +7,15 @@ block content input(id="SPIck-#{field}", type="checkbox", class="#{c}-group") dd(id="SPIda-#{field}", molochexpr=expr) - mixin spion(name, field, expr, c) - dt.context-menu-spi #{name} - input.spion(id="SPIck-#{field}", type="checkbox", class="#{c}-group") - dd(id="SPIda-#{field}", molochexpr=expr) - mixin title(name, c) - h1 #{name} - input(id="SPItck-#{c}-group", type="checkbox") + h1.collapsible(id="spiview-#{name}") #{name} + span script(src='jquery-jvectormap-1.2.2.min.js') script(src='jquery-jvectormap-world-en.js') script(src='jquery.autocomplete.js') + script(src='jquery.cookie.js') + script(src='jquery.collapsible.js') script(src='jQuery-contextMenu/src/jquery.ui.position.js') script(src='jQuery-contextMenu/src/jquery.contextMenu.js') link(rel='stylesheet', type='text/css', href='jQuery-contextMenu/src/jquery.contextMenu.css') @@ -42,12 +39,11 @@ block content each ckey in cats -var cvalue = categories[ckey] +title(ckey, ckey) - dl - each fvalue,fkey in cvalue + dl(style="display:none") + -cvalueSorted = cvalue.sort(function(a,b) {return a.friendlyName.localeCompare(b.friendlyName);}); + each fvalue,fkey in cvalueSorted - if (fvalue.noFacet || fvalue.regex) - continue - - else if (fvalue.dbField.match(/^(a1|a2|prot-term)/)) - +spion(fvalue.friendlyName, fvalue.dbField, fvalue.exp, ckey) - else if (fvalue.dbField.match(/\.snow$/)) +spi(fvalue.friendlyName, fvalue.dbField.replace(".snow", ".raw"), fvalue.exp, ckey) +spi(fvalue.friendlyName + " Tokens", fvalue.dbField, fvalue.exp, ckey) @@ -282,13 +278,25 @@ block content }); $(document).ready(function() { + + $('.collapsible').collapsible({ + cookieName: 'spiview-collapsible' + }); + + if (!molochSettings.spiviewSPIs) { + molochSettings.spiviewSPIs = ["a1", "a2", "prot-term"]; + } + var initialParams = handleUrlParams(); if (initialParams.spi) { initialParams.spi.split(",").forEach(function (item) { var parts = item.split(":"); - var spick = $("[id='SPIck-" + parts[0] + "']"); fetchSize[parts[0]] = +parts[1]; - spick.prop("checked", "checked"); + $("#SPIck-" + parts[0]).prop("checked", "checked"); + }); + } else { + molochSettings.spiviewSPIs.forEach(function(key) { + $("#SPIck-" + key).prop("checked", "checked"); }); } @@ -302,11 +310,37 @@ block content selector: '.context-menu-spi', build: function($trigger, e) { var checkbox = $(e.target).find("input"); + var field = checkbox.attr("id").substring(6); items = { - spigraph: {name: "Open SPI Graph " + $(e.target).text(), mtype:0, field: checkbox.attr("id").substring(6)}, - items: {name: "Export Unique " + $(e.target).text(), mtype:1, counts:0, field: checkbox.attr("id").substring(6)}, - itemcounts: {name: "Export Unique " + $(e.target).text() + " with Counts", mtype:1, counts:1, field: checkbox.attr("id").substring(6)}, - pivot: {name: "Pivot on " + $(e.target).text(), mtype:2, counts:0, field: checkbox.attr("id").substring(6)} + spigraph: {name: "Open SPI Graph " + $(e.target).text(), mtype:0, field: field}, + items: {name: "Export Unique " + $(e.target).text(), mtype:1, counts:0, field: field}, + itemcounts: {name: "Export Unique " + $(e.target).text() + " with Counts", mtype:1, counts:1, field: field}, + pivot: {name: "Pivot on " + $(e.target).text(), mtype:2, counts:0, field: field}, + onopen: {name: "Load by default", type: "checkbox", + selected: molochSettings.spiviewSPIs.indexOf(field) !== -1, + events: { + change: function(e) { + var checked = $(this).is(":checked"); + var pos = molochSettings.spiviewSPIs.indexOf(field); + if (checked && pos === -1) { + molochSettings.spiviewSPIs.push(field); + } else if (!checked && pos !== -1) { + molochSettings.spiviewSPIs.splice(pos, 1); + } + molochSettings.token = "#{token}"; + $.ajax( { + "dataType": 'json', + "type": "POST", + "url": "#{basePath}changeSettings", + "data": molochSettings, + "success": function(data) { + if (!data.success) { + alert(data.text); + } + } + }); + } + }} }; return { callback: function(key, options) { diff --git a/viewer/views/style.styl b/viewer/views/style.styl index 4c92688020..24963dccd9 100644 --- a/viewer/views/style.styl +++ b/viewer/views/style.styl @@ -712,7 +712,6 @@ table#sessions td.dateColumn // spiview #spiview - padding-left: 10px font-family: Arial, sans-serif background-color: rgb(220, 220, 220) @@ -774,6 +773,29 @@ table#sessions td.dateColumn #spiview h1 margin-top: 0px + margin-bottom: 3px + padding-left: 10px + +#spiview .collapsible, +#spiview .collapse-close + background: #ccc + color: #000; + +#spiview .collapse-open + background:#000; + color: #fff; + +#spiview .collapse-open span + display:block + float:right + padding:18px + background:url(minus.png) center center no-repeat + +#spiview .collapse-close span + display:block + float:right + padding:18px + background:url(plus.png) center center no-repeat // SPI Graph