r3_exception_logger 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
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
+ }