r3_exception_logger 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. data/History.txt +75 -0
  2. data/LICENSE +20 -0
  3. data/README.old +107 -0
  4. data/README.rdoc +154 -0
  5. data/Rakefile +66 -0
  6. data/VERSION.yml +5 -0
  7. data/app/controllers/logged_exceptions_controller.rb +86 -0
  8. data/app/helpers/logged_exceptions_helper.rb +40 -0
  9. data/app/models/logged_exception.rb +79 -0
  10. data/app/views/layouts/logged_exceptions.html.erb +19 -0
  11. data/app/views/logged_exceptions/_exceptions.html.erb +31 -0
  12. data/app/views/logged_exceptions/_feed.html.erb +3 -0
  13. data/app/views/logged_exceptions/_show.html.erb +23 -0
  14. data/app/views/logged_exceptions/destroy.js.erb +2 -0
  15. data/app/views/logged_exceptions/destroy_all.js.erb +2 -0
  16. data/app/views/logged_exceptions/feed.rss.builder +20 -0
  17. data/app/views/logged_exceptions/index.html.erb +46 -0
  18. data/app/views/logged_exceptions/index.js.erb +2 -0
  19. data/app/views/logged_exceptions/query.js.erb +2 -0
  20. data/app/views/logged_exceptions/show.html.erb +8 -0
  21. data/app/views/logged_exceptions/show.js.erb +2 -0
  22. data/config/initializers/date_formats.rb +5 -0
  23. data/config/locales/en.yml +16 -0
  24. data/config/routes.rb +11 -0
  25. data/exception_logger.gemspec +23 -0
  26. data/public/javascripts/exception_logger.js +69 -0
  27. data/public/stylesheets/exception_logger.css +325 -0
  28. data/r3_exception_logger-0.1.10.gem +0 -0
  29. data/test/controllers/logged_exceptions_controller_test.rb +196 -0
  30. data/test/models/logged_exception_test.rb +39 -0
  31. data/test/rails_root/Gemfile +52 -0
  32. data/test/rails_root/Gemfile.lock +146 -0
  33. data/test/rails_root/app/controllers/application_controller.rb +5 -0
  34. data/test/rails_root/test/factories/exception_logger.rb +142 -0
  35. data/test/test_helper.rb +10 -0
  36. metadata +36 -1
@@ -0,0 +1,40 @@
1
+ module LoggedExceptionsHelper
2
+ def pretty_exception_date(exception)
3
+ if Date.today == exception.created_at.to_date
4
+ if exception.created_at > Time.now - 4.hours
5
+ "#{time_ago_in_words(exception.created_at).gsub(/about /,"~ ")} ago"
6
+ else
7
+ "Today, #{exception.created_at.strftime(Time::DATE_FORMATS[:exc_time])}"
8
+ end
9
+ else
10
+ exception.created_at.strftime(Time::DATE_FORMATS[:exc_date])
11
+ end
12
+ end
13
+
14
+ def filtered?
15
+ [:query, :date_ranges_filter, :exception_names_filter, :controller_actions_filter].any? { |p| params[p] }
16
+ end
17
+
18
+ def listify(text)
19
+ list_items = text.scan(/^\s*\* (.+)/).map {|match| content_tag(:li, match.first) }
20
+ content_tag(:ul, list_items)
21
+ end
22
+
23
+ def page_title(text)
24
+ title = ""
25
+ unless controller.application_name.blank?
26
+ title << "#{controller.application_name} :: "
27
+ end
28
+ title << text.to_s
29
+ content_for(:title, title.to_s)
30
+ end
31
+
32
+ # Rescue textilize call if RedCloth is not available.
33
+ def pretty_format(text)
34
+ begin
35
+ textilize(text).html_safe
36
+ rescue
37
+ simple_format(text).html_safe
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,79 @@
1
+ class LoggedException < ActiveRecord::Base
2
+ class << self
3
+ def create_from_exception(controller, exception, data)
4
+ message = exception.message.inspect
5
+ message << "\n* Extra Data\n\n#{data}" unless data.blank?
6
+ e = create! \
7
+ :exception_class => exception.class.name,
8
+ :controller_name => controller.controller_path,
9
+ :action_name => controller.action_name,
10
+ :message => message,
11
+ :backtrace => exception.backtrace,
12
+ :request => controller.request
13
+ end
14
+
15
+ def host_name
16
+ `hostname -s`.chomp
17
+ end
18
+ end
19
+
20
+ scope :by_exception_class, lambda {|exception_class| where(:exception_class => exception_class)}
21
+ scope :by_controller_and_action, lambda {|controller_name, action_name| where(:controller_name => controller_name, :action_name => action_name)}
22
+ scope :by_controller, lambda {|controller_name| where(:controller_name => controller_name)}
23
+ scope :by_action, lambda {|action_name| where(:action_name => action_name)}
24
+ scope :message_like, lambda {|query| where(:message.matches => "%#{query}%")}
25
+ scope :days_old, lambda {|day_number| where("created_at >= ?", day_number.to_f.days.ago.utc)}
26
+ scope :sorted, order("created_at desc")
27
+
28
+ def name
29
+ "#{self.exception_class} in #{self.controller_action}"
30
+ end
31
+
32
+ def backtrace=(trace)
33
+ trace = sanitize_backtrace(trace) * "\n" unless trace.is_a?(String)
34
+ write_attribute :backtrace, trace
35
+ end
36
+
37
+ def request=(request)
38
+ if request.is_a?(String)
39
+ write_attribute :request, request
40
+ else
41
+ max = request.env.keys.max { |a,b| a.length <=> b.length }
42
+ env = request.env.keys.sort.inject [] do |env, key|
43
+ env << '* ' + ("%-*s: %s" % [max.length, key, request.env[key].to_s.strip])
44
+ end
45
+ write_attribute(:environment, (env << "* Process: #{$$}" << "* Server : #{self.class.host_name}") * "\n")
46
+
47
+ write_attribute(:request, [
48
+ "* URL:#{" #{request.method.to_s.upcase}" unless request.get?} #{request.protocol}#{request.env["HTTP_HOST"]}#{request.fullpath}",
49
+ "* Format: #{request.format.to_s}",
50
+ "* Parameters: #{request.parameters.inspect}",
51
+ "* Rails Root: #{rails_root}"
52
+ ] * "\n")
53
+ end
54
+ end
55
+
56
+ def controller_action
57
+ @controller_action ||= "#{controller_name.camelcase}/#{action_name}"
58
+ end
59
+
60
+ def self.class_names
61
+ select("DISTINCT exception_class").order(:exception_class).collect(&:exception_class)
62
+ end
63
+
64
+ def self.controller_actions
65
+ select("DISTINCT controller_name, action_name").order(:controller_name,:action_name).collect(&:controller_action)
66
+ end
67
+
68
+ private
69
+ @@rails_root = Pathname.new(Rails.root).cleanpath.to_s
70
+ @@backtrace_regex = /^#{Regexp.escape(@@rails_root)}/
71
+
72
+ def sanitize_backtrace(trace)
73
+ trace.collect { |line| Pathname.new(line.gsub(@@backtrace_regex, "[RAILS_ROOT]")).cleanpath.to_s }
74
+ end
75
+
76
+ def rails_root
77
+ @@rails_root
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= content_for?(:title) ? yield(:title) : "Exception Logger" %></title>
5
+ <meta charset="UTF-8" />
6
+ <%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js' %>
7
+ <%= javascript_include_tag 'exception_logger' %>
8
+ <%= stylesheet_link_tag 'exception_logger' %>
9
+ <%= csrf_meta_tag %>
10
+
11
+ <%= auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "RSS Feed"}) %>
12
+ </head>
13
+ <body>
14
+ <div id="container">
15
+ <%= yield %>
16
+ </div>
17
+ <br style="clear:both" />
18
+ </body>
19
+ </html>
@@ -0,0 +1,31 @@
1
+ <div id="exceptions">
2
+ <div class="pages">
3
+ <%= link_to 'Delete Visible', { :action => 'destroy_all' }, :class => 'delete_visible_link' %>
4
+ <strong><%= paginate @exceptions, :style => 'display:inline' %></strong>
5
+ </div>
6
+ <h1>Exceptions <%= raw "<span>(filtered)</span>" if filtered? %> </h1>
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th>Exception</th>
11
+ <th>Date</th>
12
+ <th></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% @exceptions.each do |exception| %>
17
+ <tr id="<%= dom_id(exception) %>" class="<%= cycle("eor", "") %> exception">
18
+ <td>
19
+ <div class="expclass"><%= link_to exception.name, {:action => "show", :id => exception}, :class => 'show_link' %></div>
20
+ <span class="message"><%= h exception.message %></span>
21
+ </td>
22
+ <td class="time"><%= pretty_exception_date(exception) %></td>
23
+ <td><%= link_to 'Delete', { :action => 'destroy', :id => exception }, :method => :delete, :class => "util delete_link" %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
28
+ <div class="pages pages-bottom">
29
+ <strong><%= paginate @exceptions, :style => 'display:inline' %></strong>
30
+ </div>
31
+ </div> <!-- #exceptions -->
@@ -0,0 +1,3 @@
1
+ <ul id="feed" class="filters">
2
+ <li><%= link_to "RSS Feed", feed_logged_exceptions_url(:format => :rss) %></li>
3
+ </ul>
@@ -0,0 +1,23 @@
1
+ <div class="tools">
2
+ <%= link_to 'Delete', { :action => 'destroy', :id => @exception }, :class => "util delete_link" %>
3
+ <span class="pipe">|</span>
4
+ <%= link_to "Close", "#", :class => "util close_link" %>
5
+ </div>
6
+
7
+ <div class="date">
8
+ <%= @exception.created_at.strftime(Time::DATE_FORMATS[:exc_full]) %>
9
+ </div>
10
+ <h1><%= @exception.name %></h1>
11
+
12
+ <h2>Request</h2>
13
+ <%= pretty_format(@exception.request) %>
14
+
15
+ <h2>Backtrace</h2>
16
+ <%= simple_format @exception.message %>
17
+
18
+ <div id="backtrace">
19
+ <%= raw @exception.backtrace.gsub(/\n/,"<br />") %>
20
+ </div>
21
+
22
+ <h2>Environment</h2>
23
+ <%= pretty_format(@exception.environment) %>
@@ -0,0 +1,2 @@
1
+ $('#<%= dom_id(@exception) %>').addClass('deleted');
2
+ $('#showpage').hide();
@@ -0,0 +1,2 @@
1
+ $('#exceptions').html('<%= escape_javascript render :partial => "exceptions" %>');
2
+ $('#showpage').hide();
@@ -0,0 +1,20 @@
1
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
2
+
3
+ xml.rss "version" => "2.0" do
4
+ xml.channel do
5
+ xml.title "#{LoggedExceptionsController.application_name}"
6
+ xml.link url_for(:only_path => false, :skip_relative_url_root => false)
7
+ xml.language "en-us"
8
+ xml.ttl "60"
9
+
10
+ @exceptions.each do |exc|
11
+ xml.item do
12
+ xml.title "#{exc.name} @ #{exc.created_at.rfc822}"
13
+ xml.description exc.message
14
+ xml.pubDate exc.created_at.rfc822
15
+ xml.guid [request.host_with_port, 'exceptions', exc.id.to_s].join(":"), "isPermaLink" => "false"
16
+ xml.link url_for(:action => 'index', :anchor => "e#{exc.id}", :only_path => false, :skip_relative_url_root => false)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ <% page_title t(".title") %>
2
+ <div id="right">
3
+ <h3><%=t(".filters")%></h3>
4
+ <ul id="all_exceptions" class="filters">
5
+ <li><%= link_to t(".latest_exceptions"), :action => 'index', :id => nil %></li>
6
+ </ul>
7
+ <h4><%=t(".exception")%></h4>
8
+ <ul id="exception_names" class="filters">
9
+ <% @exception_names.each do |name| %>
10
+ <li><%= link_to name, {:action => 'query', :exception_names_filter => "#{name}"}, :class => 'filter_link' %></li>
11
+ <% end %>
12
+ </ul>
13
+ <h4><%=t(".controller_action")%></h4>
14
+ <ul id="controller_actions" class="filters">
15
+ <% @controller_actions.each do |action| %>
16
+ <li><%= link_to action, {:action => 'query', :controller_actions_filter => "#{action}"}, :class => 'filter_link' %></li>
17
+ <% end %>
18
+ </ul>
19
+ <h4><%=t(".dates")%></h4>
20
+ <ul id="date_ranges" class="filters">
21
+ <li><%= link_to t(".today"), {:action => 'query', :date_ranges_filter => "1"}, :title => "1", :class => 'filter_link' %></li>
22
+ <li><%= link_to t(".last_few_days"), {:action => 'query', :date_ranges_filter => "3"}, :title => "3", :class => 'filter_link' %></li>
23
+ <li><%= link_to t(".last_7_days"), {:action => 'query', :date_ranges_filter => "7"}, :title => "7", :class => 'filter_link' %></li>
24
+ <li><%= link_to t(".last_30_days"), {:action => 'query', :date_ranges_filter => "30"}, :title => "30", :class => 'filter_link' %></li>
25
+ </ul>
26
+ <div id="search">
27
+ <%= form_tag "logged_exceptions/query", :id => 'query-form' %>
28
+ <div>
29
+ <%= text_field_tag :query, "", :size => 17 %>
30
+ <%= submit_tag :Find %>
31
+ <%= hidden_field_tag :exception_names_filter %>
32
+ <%= hidden_field_tag :date_ranges_filter %>
33
+ <%= hidden_field_tag :controller_actions_filter %>
34
+ <%= hidden_field_tag :page, (params[:page] || 1) %>
35
+ </div>
36
+ </form>
37
+ </div>
38
+ <%= render :partial => 'feed' %>
39
+ </div> <!-- right -->
40
+ <div id="left">
41
+ <div class="page" id="showpage" style="display:none;"></div>
42
+ <div class="page">
43
+ <%= render :partial => "exceptions" %>
44
+ </div>
45
+ </div>
46
+ <div id="activity" style="display:none;">Busy...</div>
@@ -0,0 +1,2 @@
1
+ $('#exceptions').html('<%= escape_javascript render :partial => "exceptions" %>');
2
+ $('#showpage').hide();
@@ -0,0 +1,2 @@
1
+ $('#exceptions').html('<%= escape_javascript render :partial => "exceptions" %>');
2
+ $('#showpage').hide();
@@ -0,0 +1,8 @@
1
+ <% page_title t(".title") %>
2
+ <div id="right">
3
+ </div> <!-- right -->
4
+ <div id="left">
5
+ <div class="page" id="showpage">
6
+ <%= render :partial => "show" %>
7
+ </div>
8
+ </div>
@@ -0,0 +1,2 @@
1
+ $('#showpage').html('<%= escape_javascript render :partial => "show" %>');
2
+ $('#showpage').show();
@@ -0,0 +1,5 @@
1
+ Time::DATE_FORMATS.merge!(
2
+ :exc_full => "%A, %b %d, %Y at %l:%M %p",
3
+ :exc_date => "%b %d, %Y",
4
+ :exc_time => "%l:%M %p"
5
+ )
@@ -0,0 +1,16 @@
1
+ en:
2
+ logged_exceptions:
3
+ index:
4
+ title: Logged Exceptions
5
+ filters: Filters
6
+ latest_exceptions: Latest Exceptions
7
+ exception: Exception
8
+ controller_action: Controller / Action
9
+ dates: Dates
10
+ today: Today
11
+ last_few_days: Last few days
12
+ last_7_days: Last 7 days
13
+ last_30_days: Last 30 days
14
+ show:
15
+ title: Logged Exception
16
+
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Rails.application.routes.draw do
2
+
3
+ # Exception Logger
4
+ resources :logged_exceptions do
5
+ collection do
6
+ post :query
7
+ post :destroy_all
8
+ get :feed
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'bundler/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "r3_exception_logger"
9
+ s.version = "0.1.11"
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Chris Wise"]
12
+ s.email = ["cwise@murmurinformatics.com"]
13
+ s.homepage = "http://github.com/cwise/exception_logger"
14
+ s.summary = "Fork of exception_logger gem"
15
+ s.description = "Fork of exception_logger gem"
16
+
17
+ s.required_rubygems_version = ">= 1.3.6"
18
+
19
+ s.files = Dir.glob("**/*") + Dir.glob("{config}/**/*")
20
+ # s.files = `git ls-files -- {lib/*,vendor/*,*.gemspec}`.split("\n")
21
+ # s.files = `git ls-files -- {lib/*,app/*,config/*,public/*,vendor/*,*.gemspec}`.split("\n")
22
+ s.require_path = 'lib'
23
+ end
@@ -0,0 +1,69 @@
1
+ $(function () {
2
+ function CSRFProtection(xhr) {
3
+ var token = $('meta[name="csrf-token"]').attr('content');
4
+ if (token) xhr.setRequestHeader('X-CSRF-Token', token);
5
+ };
6
+
7
+ function ajax_headers() {
8
+ if ('ajaxPrefilter' in $) $.ajaxPrefilter(function(options, originalOptions, xhr){ CSRFProtection(xhr) });
9
+ else $(document).ajaxSend(function(e, xhr){ CSRFProtection(xhr) });
10
+ };
11
+
12
+ ajax_headers();
13
+
14
+ $('.show_link').live('click', function(event) {
15
+ $.get($(this).attr("href"));
16
+ return false;
17
+ });
18
+
19
+ $('.close_link').live('click', function(event) {
20
+ $("#showpage").hide();
21
+ return false;
22
+ });
23
+
24
+ $('.delete_link').live('click', function(event) {
25
+ $.ajax({
26
+ url: $(this).attr("href"),
27
+ type: 'DELETE'
28
+ });
29
+ return false;
30
+ });
31
+
32
+ $('.delete_visible_link').live('click', function(event) {
33
+ var arr=$('tr.exception').map(function() { var id = $(this).attr("id"); return parseInt(id.replace(/^\w+_/, '')); }).get();
34
+ $.ajax({
35
+ url: $(this).attr("href"),
36
+ type: 'POST',
37
+ data: $.param({ids: arr}),
38
+ dataType: 'script'
39
+ });
40
+ return false;
41
+ });
42
+
43
+ $('.filter_link').live('click', function(event) {
44
+ $('.filter_link').removeClass('selected');
45
+ $(this).addClass('selected');
46
+ $.ajax({
47
+ url: $(this).attr("href"),
48
+ type: 'POST',
49
+ dataType: 'script'
50
+ });
51
+ return false;
52
+ });
53
+
54
+ $('#query-form :submit').live('click', function(event) {
55
+ $.ajax({
56
+ url: $("#query-form").attr("action"),
57
+ type: 'POST',
58
+ data: $("#query-form").serialize(),
59
+ dataType: 'script'
60
+ });
61
+ return false;
62
+ });
63
+
64
+ $(".pagination a").live("click", function() {
65
+ $.getScript(this.href);
66
+ return false;
67
+ });
68
+ });
69
+
@@ -0,0 +1,325 @@
1
+ body
2
+ {
3
+ margin:0;
4
+ padding:0;
5
+ background:#000;
6
+ font-family:'Lucida Grande', Arial, Helvetica, sans-serif;
7
+ font-size:1.0em;
8
+ color:white;
9
+ }
10
+
11
+ #container
12
+ {
13
+ xwidth:95%;
14
+ margin:0 auto;
15
+ min-width:800px;
16
+ }
17
+
18
+ #left
19
+ {
20
+ xfloat:left;
21
+ xwidth:76%;
22
+ margin-right:235px;
23
+
24
+ }
25
+
26
+ #left .page
27
+ {
28
+ font-size:0.9em;
29
+ background:white;
30
+ border:2px solid #999;
31
+ border-width:0 2px 2px 0;
32
+ padding:25px;
33
+ xmargin-bottom:1em;
34
+ color:black;
35
+ }
36
+
37
+ #right
38
+ {
39
+ margin-top:1em;
40
+ float:right;
41
+ xwidth:22%;
42
+ font-size:0.9em;
43
+ width:200px;
44
+ margin-right:1.25em;
45
+ }
46
+
47
+ #right hr
48
+ {
49
+ border:0;
50
+ border-top:1px solid #222;
51
+ }
52
+
53
+ #search
54
+ {
55
+ margin-top:2em;
56
+ background:#111;
57
+ padding:10px 5px;
58
+ border:1px solid #222;
59
+ border-width:1px 0;
60
+ }
61
+
62
+
63
+ ul.filters
64
+ {
65
+ list-style-type:none;
66
+ padding:0;
67
+ margin:0;
68
+ font-size:0.9em;
69
+ }
70
+ ul.filters li { margin-bottom:0.2em;}
71
+
72
+
73
+ ul.filters a,
74
+ ul.filters a:visited {
75
+ display:block;
76
+ padding:1px 7px;
77
+ color:#fff;
78
+ xbackground-color:#fff;
79
+ text-decoration:none;
80
+ }
81
+
82
+ ul.filters a:hover
83
+ {
84
+ background:#666;
85
+ color:white;
86
+ }
87
+
88
+ ul.filters a.selected,
89
+ ul.filters a.selected:visited,
90
+ ul.filters a:active {
91
+ color:gold;
92
+ background-color:#333;
93
+ text-decoration:none;
94
+ font-weight:bold;
95
+ }
96
+
97
+
98
+ onclick a:hover
99
+ {
100
+ color:#fff;
101
+ text-decoration:none;
102
+ }
103
+
104
+ #exceptions table
105
+ {
106
+ width:99%;
107
+ border-collapse:collapse;
108
+ }
109
+
110
+ td
111
+ {
112
+ padding:5px 10px;
113
+ xborder:1px solid #ddd;
114
+ }
115
+
116
+ #backtrace
117
+ {
118
+ overflow:auto;
119
+ font-family:"Bitstream Vera Sans Mono", "Monaco", "Courier", monospace;;
120
+ font-size:0.85em;
121
+ margin-top:0.25em;
122
+ }
123
+
124
+ h2
125
+ {
126
+ background-color:#ddd;
127
+ font-size:0.8em;
128
+ padding:3px 10px;
129
+ }
130
+
131
+ form {margin:0;}
132
+
133
+ h3 {
134
+ font-size:0.8em;
135
+ color:#ddd;
136
+ background:#222;
137
+ padding:3px 7px;
138
+ }
139
+
140
+ div.date
141
+ {
142
+ color:#666;
143
+ font-size:0.8em;
144
+ }
145
+
146
+ h1
147
+ {
148
+ margin-top:0.25em;
149
+ font-size:1.25em;
150
+ padding-bottom:5px;
151
+ border-bottom:2px solid #ddd;
152
+ }
153
+
154
+ h1 span
155
+ {
156
+ color:#aaa;
157
+ font-weight:normal;
158
+ }
159
+
160
+ a
161
+ {
162
+ color:#369;
163
+ text-decoration:none;
164
+ }
165
+ a:hover
166
+ {
167
+ color:blue;
168
+ text-decoration:underline;
169
+ }
170
+
171
+ th
172
+ {
173
+ text-align:left;
174
+ xbackground:#333;
175
+ xcolor:gold;
176
+ font-size:0.75em;
177
+ padding:2px 10px;
178
+ }
179
+
180
+ tr { xcursor:pointer; }
181
+
182
+ tr.eor td
183
+ {
184
+ background:#e7e7e7;
185
+
186
+ }
187
+
188
+ /*
189
+ tr:hover td,
190
+ tr.eor:hover td
191
+ {
192
+ background:#333;
193
+ color:white;
194
+ }
195
+ tr:hover td a,
196
+ tr.eor:hover td a { color:gold; }
197
+ */
198
+
199
+ .message
200
+ {
201
+ font-size:0.8em;
202
+ }
203
+
204
+ a.util
205
+ {
206
+ color:#c00;
207
+ font-size:0.7em;
208
+ }
209
+
210
+ .pipe
211
+ {
212
+ font-size:0.75em;
213
+ color:#999;
214
+ }
215
+
216
+ .tools { float:right; }
217
+
218
+ .time
219
+ {
220
+ color:#666;
221
+ font-size:0.75em;
222
+ xvertical-align:top;
223
+ }
224
+
225
+
226
+ .expclass
227
+ {
228
+ xcolor:#999;
229
+ }
230
+ .expclass a
231
+ {
232
+ font-size:0.9em;
233
+ }
234
+
235
+ tr.deleted td {
236
+ color:#aaa;
237
+ text-decoration: line-through;
238
+ }
239
+ tr.deleted td a { color:#aaa; }
240
+
241
+ .pages { float:right; margin-right:1em; }
242
+ .pages a { text-decoration:underline; }
243
+ .pages-bottom { border-top:2px solid #ddd; text-align:right; float:none;
244
+ padding-top:0.4em;
245
+ margin-top:0.4em;
246
+ padding-right:1em;
247
+ margin-right:0;
248
+ }
249
+
250
+ /* right */
251
+
252
+ #right h4
253
+ {
254
+ font-size:0.75em;
255
+ xbackground:#171717;
256
+ xbackground:#333;
257
+ color:#999;
258
+ padding:3px 5px;
259
+ margin-bottom:0.5em;
260
+ font-weight:normal;
261
+ }
262
+
263
+ /* tabs */
264
+
265
+ ul.tabs
266
+ {
267
+ list-style-type:none;
268
+ padding:0;
269
+ margin:1em 0;
270
+ float:left;
271
+ }
272
+ ul.tabs li { float:left; display:inline; }
273
+ ul.tabs li a
274
+ {
275
+ font-size:0.8em;
276
+ padding:3px 7px;
277
+ margin-right:1em;
278
+ text-decoration:none;
279
+ color:black;
280
+ }
281
+
282
+ ul.tabs li a:hover
283
+ {
284
+ background:#666;
285
+ color:white;
286
+ }
287
+ ul.tabs li.selected a
288
+ {
289
+ background:black;
290
+ color:white;
291
+ }
292
+
293
+ #activity {
294
+ position:fixed;
295
+ top:0; right:18px;
296
+ width:200px;
297
+ padding:5px 0;
298
+ text-align:center;
299
+ background-color:#cf1313;
300
+ color:#fff;
301
+ opacity:.8;
302
+ font-size: 11px;
303
+ }
304
+
305
+ #feed {
306
+ margin-top: 15px;
307
+ }
308
+
309
+ /* html5 changes */
310
+
311
+ #exceptions table {
312
+ border-collapse: collapse;
313
+ }
314
+
315
+ #exceptions table thead {
316
+ display: none;
317
+ }
318
+
319
+ #exceptions table tbody tr.exception td.time {
320
+ white-space: nowrap
321
+ }
322
+
323
+ #container #left #showpage {
324
+ margin-bottom:1em;
325
+ }