diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..677c465 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.bundle diff --git a/.versions.conf b/.versions.conf new file mode 100644 index 0000000..f2cdbd3 --- /dev/null +++ b/.versions.conf @@ -0,0 +1,2 @@ +ruby=ruby-2.1.0 +ruby-gemset=grokdebug diff --git a/public/sticky.css b/public/sticky.css index d3482b7..ab671e9 100644 --- a/public/sticky.css +++ b/public/sticky.css @@ -2,6 +2,10 @@ html, body, .container, .container-fluid, .content { height: 100%; } +label[for]:not([for=""]) { + cursor: pointer; +} + .container, .container-fluid, .content { position: relative; } @@ -17,10 +21,63 @@ html, body, .container, .container-fluid, .content { margin: 0 auto -50px; /* same as the footer */ } +.text-right { + text-align: right; +} + .push { height: 50px; /* same as the footer */ } .footer-wrapper { position: relative; height: 50px; +} + +/* Animated rotating icon */ +.icon-spin { + display: inline-block; + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + } + 100% { + -moz-transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -o-transform: rotate(0deg); + } + 100% { + -o-transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -ms-transform: rotate(0deg); + } + 100% { + -ms-transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } } \ No newline at end of file diff --git a/views/discover.haml b/views/discover.haml index ea86562..51d3564 100644 --- a/views/discover.haml +++ b/views/discover.haml @@ -5,13 +5,15 @@ .control-group .controls %form{ :action => "#", :class => "form-horizontal", :id => "grok-form"} - %textarea{ :id => "input", :rows => "5", :action => "#", :class => "span9" } - %button{ :type => "submit", :class => "btn btn-primary"} - Discover - %button{ :class => "btn btn-danger", :type => "button", :id => "resetall" } - Reset - .well-large - %pre{ :class => "span9", :id => "grok"} + %textarea{ :id => "input", :rows => "5", :action => "#", :class => "span12" } + %br   + .text-right + %button{ :type => "submit", :class => "btn btn-primary"} + Discover + %button{ :class => "btn btn-danger", :type => "button", :id => "resetall" } + Reset + .well + %pre{ :id => "grok"} .push .footer-wrapper %footer diff --git a/views/index.haml b/views/index.haml index 21de2c6..377f2bd 100644 --- a/views/index.haml +++ b/views/index.haml @@ -1,30 +1,33 @@ .container .content .wrapper - .proper-content + .proper-content %form{ :action => "#", :id => "grok-form" } - %textarea{ :type => "text", :class => "span12", :placeholder => "Input", :id => "input" } + %textarea{ :type => "text", :class => "span12", :placeholder => "Input", :id => "input", :rows => "5" } .ui-widget - %textarea{ :type => "text", :class => "span12", :placeholder => "Pattern", :id => "tags" } - %label{ :class => "checkbox inline",} + %textarea{ :type => "text", :class => "span12", :placeholder => "Pattern", :id => "tags", :rows => "5" } + %label{ :for => "add_custom_patterns", :class => "checkbox inline",} %input{ :id => "add_custom_patterns", :type => "checkbox", :value= => "" } Add custom patterns - %label{ :class => "checkbox inline",} + %label{ :for => "keep_empty_captures", :class => "checkbox inline",} %input{ :id => "keep_empty_captures", :type => "checkbox", :value= => "" } Keep Empty Captures - %label{ :class => "checkbox inline" } + %label{ :for => "named_captures_only", :class => "checkbox inline" } %input{ :id => "named_captures_only", :type => "checkbox", :value => "" } Named Captures Only - %label{ :class => "checkbox inline" } + %label{ :for => "singles", :class => "checkbox inline" } %input{ :id => "singles", :type => "checkbox", :value => "" } Singles - %label{ :class => "checkbox inline pull-right" } - %input{ :id => "autocomplete", :type => "checkbox", :value => "" } - Autocomplete + %span{ :class => "pull-right" } + %label{ :for => "autocomplete", :class => "checkbox inline" } + %input{ :id => "autocomplete", :type => "checkbox", :value => "" } + Autocomplete +   + %input{ :type => "submit", :class => "inline btn btn-primary", :value => "Go"} %div{:id => "custom_patterns_container", :style=>"display:none;"} %p One per line, the syntax for a grok pattern is %{SYNTAX:SEMANTIC} - %textarea{ :class => "span12", :placeholder => "Custom patterns", :id => "custom_patterns", } + %textarea{ :class => "span12", :placeholder => "Custom patterns", :id => "custom_patterns", :rows => "5" } %div{ :class => "well" } %pre{ :id => "grok" } .push @@ -36,81 +39,145 @@ %script{ :type => "text/javascript", :src => "//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/js/bootstrap.min.js" } %script{ :type => "text/javascript", :src => "//ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js" } :javascript + // function to load settings from localstorage + var loadLocalstorage = function() { + $('#input').val(localStorage.getItem('input')); + $('#tags').val(localStorage.getItem('pattern')); + $('#add_custom_patterns').prop('checked', parseInt(localStorage.getItem('add_custom_patterns'))); + $('#named_captures_only').prop('checked', parseInt(localStorage.getItem('named_captures_only'))); + $('#keep_empty_captures').prop('checked', parseInt(localStorage.getItem('keep_empty_captures'))); + $('#singles').prop('checked', parseInt(localStorage.getItem('singles'))); + $('#autocomplete').prop('checked', parseInt(localStorage.getItem('autocomplete'))); + + // show custom pattern window and fill valueif needed + if ($('#add_custom_patterns').is(':checked')) { + $('#custom_patterns').val(localStorage.getItem('custom_patterns')); + $('#custom_patterns_container').show(); + } + }; + // function to save settings to localstorage + var saveLocalstorage = function(custom_patterns, input, pattern, add_custom_patterns, named_captures_only, keep_empty_captures, singles, autocomplete) { + localStorage.setItem('custom_patterns', custom_patterns); + localStorage.setItem('input', input); + localStorage.setItem('pattern', pattern); + localStorage.setItem('add_custom_patterns', (add_custom_patterns == true ? 1 : 0)); + localStorage.setItem('named_captures_only', (named_captures_only == true ? 1 : 0)); + localStorage.setItem('keep_empty_captures', (keep_empty_captures == true ? 1 : 0)); + localStorage.setItem('singles', (singles == true ? 1 : 0)); + localStorage.setItem('autocomplete', (autocomplete == true ? 1 : 0)); + }; + + // function to execute AJAX matching var match = function() { - var custom_patterns = $('#custom_patterns').val(); + var custom_patterns = $('#custom_patterns').val(); var input = $('#input').val(); var pattern = $('#tags').val(); + var add_custom_patterns = $('#add_custom_patterns').is(':checked'); var named_captures_only = $('#named_captures_only').is(':checked'); var keep_empty_captures = $('#keep_empty_captures').is(':checked'); var singles = $('#singles').is(':checked'); - + var autocomplete = $('#autocomplete').is(':checked'); + + // Save the user's settings to localstorage for next time + saveLocalstorage(custom_patterns, input, pattern, add_custom_patterns, named_captures_only, keep_empty_captures, singles, autocomplete); + + // Display a loading indicator + $('#grok').parent().prepend(""); + + // Get the results from Grok $.post('/grok', { - "custom_patterns":custom_patterns, + "custom_patterns":custom_patterns, "input":input, "pattern":pattern, "named_captures_only":named_captures_only, "keep_empty_captures":keep_empty_captures, "singles":singles }, function(data){ - $('#grok').html(data); - }); + // Display the results to the user + $('#grok').html(data); + + // Remove the loading indicator after a short time + setTimeout(function(){$('#grokSpinner').remove();}, 500); + } + ); }; + + // load settings from localstorage + loadLocalstorage(); + + // go ahead and match just in case we have something already + match(); + + // call match() if pattern, input, or custom patterns change var oldPat = $("#tags").val(); var oldIn = $("#input").val(); + var oldCust = $("#custom_patterns").val(); setInterval(function (){ pat = $("#tags").val(); inp = $("#input").val(); - if(inp == oldIn && pat == oldPat){ + cust = $("#custom_patterns").val(); + if(inp == oldIn && pat == oldPat && cust == oldCust){ return; } else { oldPat = pat; oldIn = inp; + oldCust = cust; match(); } }, 1000); - $("#add_custom_patterns").click(function(){ - $( "#custom_patterns_container" ).toggle(function() { - $( "#custom_patterns" ).val("") - }) - }) + + // also match if various buttons are clicked $("#named_captures_only").click(function(){ - match(); + match(); }) $("#keep_empty_captures").click(function(){ - match(); + match(); }) $("#singles").click(function(){ match(); }) + $("#grok-form").submit(function(){ + match(); + return false; + }); + + // show/hide custom patterns container, and remove patterns + $("#add_custom_patterns").click(function(){ + $( "#custom_patterns_container" ).toggle(function() { + $( "#custom_patterns" ).val("") + }) + }) + + + // autocomplete functionality $("#autocomplete").click(function(){ var autocomplete = $('#autocomplete').is(':checked'); $( "#tags" ).autocomplete({ disabled: !autocomplete }); }); - $("#grok-form").submit(match); var availableTags = #{@tags}; function split( val ) { - return val.split( /}\s*/ ); + return val.split( /}\s*/ ); } function extractLast( term ) { - return split( term ).pop(); + return split( term ).pop(); } $( "#tags" ) - // don't navigate away from the field on tab when selecting an item - .bind( "keydown", function( event ) { - if ( event.keyCode === $.ui.keyCode.TAB && - $( this ).data( "autocomplete" ).menu.active ) { - event.preventDefault(); - } - }) - .autocomplete({ - minLength: 3, - disabled: true, - source: function( request, response ) { - $.each($( "#custom_patterns" ).val().split('\n'), function(){ + // don't navigate away from the field on tab when selecting an item + .bind( "keydown", function( event ) { + if ( event.keyCode === $.ui.keyCode.TAB && + $( this ).data( "autocomplete" ).menu.active ) { + event.preventDefault(); + } + }) + .autocomplete({ + minLength: 3, + disabled: true, + source: function( request, response ) { + $.each($( "#custom_patterns" ).val().split('\n'), function(){ var line_splitted = this.replace(/^\s*/, "").split(/\s+/) var name = line_splitted[0] if ( $.inArray(name, availableTags) == -1 ) { @@ -118,23 +185,23 @@ } }); - // delegate back to autocomplete, but extract the last term - response( $.ui.autocomplete.filter( - availableTags, extractLast( request.term ) ) ); - }, - focus: function() { - // prevent value inserted on focus - return false; - }, - select: function( event, ui ) { - var terms = split( this.value ); - // remove the current input - terms.pop(); - // add the selected item - terms.push( ui.item.value ); - // add placeholder to get the comma-and-space at the end - terms.push( "" ); - this.value = terms.join( "}" ); - return false; - } - }) + // delegate back to autocomplete, but extract the last term + response( $.ui.autocomplete.filter( + availableTags, extractLast( request.term ) ) ); + }, + focus: function() { + // prevent value inserted on focus + return false; + }, + select: function( event, ui ) { + var terms = split( this.value ); + // remove the current input + terms.pop(); + // add the selected item + terms.push( ui.item.value ); + // add placeholder to get the comma-and-space at the end + terms.push( "" ); + this.value = terms.join( "}" ); + return false; + } + }) diff --git a/views/layout.haml b/views/layout.haml index 5c735c2..53e3807 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -15,7 +15,7 @@ %link{ :href => "//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap.min.css", :rel => "stylesheet" } %link{ :href => "//ajax.googleapis.com/ajax/libs/jqueryui/1.9.0/themes/ui-lightness/jquery-ui.css", :rel => "stylesheet" } %link{ :href => "sticky.css", :rel => "stylesheet"} - / Le fav and touch icons + / Le fav and touch icons %link{ rel: "apple-touch-icon", href: "/bootstrap/images/apple-touch-icon.png"} %link{ rel: "apple-touch-icon", sizes: "72x72", href: "/bootstrap/images/apple-touch-icon-72x72.png"} %link{ rel: "apple-touch-icon", sizes: "114x114", href: "/bootstrap/images/apple-touch-icon-114x114.png"} diff --git a/views/navbar.haml b/views/navbar.haml index 44e8307..982dc0d 100644 --- a/views/navbar.haml +++ b/views/navbar.haml @@ -11,7 +11,7 @@ %li{:class => ('active' if request.path_info == '/')} %a{:href => '/'}Debugger %li{:class => ('active' if request.path_info == '/discover')} - %a{:href => '/discover?#'} Discover + %a{:href => '/discover?#', :target => '_blank'} Discover %li{:class => ('active' if request.path_info == '/patterns')} - %a{:href => '/patterns'} Patterns + %a{:href => '/patterns', :target => '_blank'} Patterns %p.navbar-text.pull-right