wycats-merb-core 0.9.8

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 (98) hide show
  1. data/CHANGELOG +992 -0
  2. data/CONTRIBUTORS +94 -0
  3. data/LICENSE +20 -0
  4. data/PUBLIC_CHANGELOG +142 -0
  5. data/README +21 -0
  6. data/Rakefile +458 -0
  7. data/TODO +0 -0
  8. data/bin/merb +11 -0
  9. data/bin/merb-specs +5 -0
  10. data/lib/merb-core.rb +598 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +717 -0
  13. data/lib/merb-core/config.rb +305 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +568 -0
  16. data/lib/merb-core/controller/exceptions.rb +315 -0
  17. data/lib/merb-core/controller/merb_controller.rb +256 -0
  18. data/lib/merb-core/controller/mime.rb +107 -0
  19. data/lib/merb-core/controller/mixins/authentication.rb +123 -0
  20. data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
  21. data/lib/merb-core/controller/mixins/controller.rb +319 -0
  22. data/lib/merb-core/controller/mixins/render.rb +513 -0
  23. data/lib/merb-core/controller/mixins/responder.rb +469 -0
  24. data/lib/merb-core/controller/template.rb +254 -0
  25. data/lib/merb-core/core_ext.rb +9 -0
  26. data/lib/merb-core/core_ext/hash.rb +7 -0
  27. data/lib/merb-core/core_ext/kernel.rb +340 -0
  28. data/lib/merb-core/dispatch/cookies.rb +130 -0
  29. data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
  30. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +176 -0
  34. data/lib/merb-core/dispatch/request.rb +729 -0
  35. data/lib/merb-core/dispatch/router.rb +151 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +566 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +191 -0
  39. data/lib/merb-core/dispatch/router/route.rb +511 -0
  40. data/lib/merb-core/dispatch/session.rb +222 -0
  41. data/lib/merb-core/dispatch/session/container.rb +74 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +173 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +68 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +99 -0
  45. data/lib/merb-core/dispatch/session/store_container.rb +150 -0
  46. data/lib/merb-core/dispatch/worker.rb +28 -0
  47. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  48. data/lib/merb-core/logger.rb +203 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +25 -0
  51. data/lib/merb-core/rack/adapter.rb +44 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  64. data/lib/merb-core/rack/middleware.rb +20 -0
  65. data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
  66. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  67. data/lib/merb-core/rack/middleware/csrf.rb +73 -0
  68. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  69. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  70. data/lib/merb-core/rack/middleware/static.rb +45 -0
  71. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  72. data/lib/merb-core/server.rb +284 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +229 -0
  75. data/lib/merb-core/tasks/merb.rb +1 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +11 -0
  79. data/lib/merb-core/test/helpers.rb +9 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +393 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +39 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +9 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
  89. data/lib/merb-core/test/run_specs.rb +49 -0
  90. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  91. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  92. data/lib/merb-core/test/test_ext/object.rb +14 -0
  93. data/lib/merb-core/test/test_ext/string.rb +14 -0
  94. data/lib/merb-core/vendor/facets.rb +2 -0
  95. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  96. data/lib/merb-core/vendor/facets/inflect.rb +342 -0
  97. data/lib/merb-core/version.rb +3 -0
  98. metadata +253 -0
@@ -0,0 +1,130 @@
1
+ module Merb
2
+
3
+ class Cookies < Mash
4
+
5
+ def initialize(constructor = {})
6
+ @_options_lookup = Mash.new
7
+ @_cookie_defaults = { "domain" => Merb::Controller._default_cookie_domain, "path" => '/' }
8
+ super constructor
9
+ end
10
+
11
+ # Implicit assignment of cookie key and value.
12
+ #
13
+ # ==== Parameters
14
+ # name<~to_s>:: Name of the cookie.
15
+ # value<~to_s>:: Value of the cookie.
16
+ #
17
+ # ==== Notes
18
+ # By using this method, a cookie key is marked for being
19
+ # included in the Set-Cookie response header.
20
+ def []=(key, value)
21
+ @_options_lookup[key] ||= {}
22
+ super
23
+ end
24
+
25
+ # Explicit assignment of cookie key, value and options
26
+ #
27
+ # ==== Parameters
28
+ # name<~to_s>:: Name of the cookie.
29
+ # value<~to_s>:: Value of the cookie.
30
+ # options<Hash>:: Additional options for the cookie (see below).
31
+ #
32
+ # ==== Options (options)
33
+ # :path<String>:: The path for which this cookie applies. Defaults to "/".
34
+ # :expires<Time>:: Cookie expiry date.
35
+ # :domain<String>:: The domain for which this cookie applies.
36
+ # :secure<Boolean>:: Security flag.
37
+ #
38
+ # ==== Notes
39
+ # By using this method, a cookie key is marked for being
40
+ # included in the Set-Cookie response header.
41
+ def set_cookie(name, value, options = {})
42
+ @_options_lookup[name] = options
43
+ self[name] = value
44
+ end
45
+
46
+ # Removes the cookie on the client machine by setting the value to an empty
47
+ # string and setting its expiration date into the past.
48
+ #
49
+ # ==== Parameters
50
+ # name<~to_s>:: Name of the cookie to delete.
51
+ # options<Hash>:: Additional options to pass to +set_cookie+.
52
+ def delete(name, options = {})
53
+ set_cookie(name, "", options.merge("expires" => Time.at(0)))
54
+ end
55
+
56
+ # Generate any necessary headers.
57
+ #
58
+ # ==== Returns
59
+ # Hash:: The headers to set, or an empty array if no cookies are set.
60
+ def extract_headers(controller_defaults = {})
61
+ defaults = @_cookie_defaults.merge(controller_defaults)
62
+ cookies = []
63
+ self.each do |name, value|
64
+ # Only set cookies that marked for inclusion in the response header.
65
+ next unless @_options_lookup[name]
66
+ options = defaults.merge(@_options_lookup[name])
67
+ if (expiry = options["expires"]).respond_to?(:gmtime)
68
+ options["expires"] = expiry.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
69
+ end
70
+ secure = options.delete("secure")
71
+ kookie = "#{name}=#{Merb::Request.escape(value)}; "
72
+ # WebKit in particular doens't like empty cookie options - skip them.
73
+ options.each { |k, v| kookie << "#{k}=#{v}; " unless v.blank? }
74
+ kookie << 'secure' if secure
75
+ cookies << kookie.rstrip
76
+ end
77
+ cookies.empty? ? {} : { 'Set-Cookie' => cookies }
78
+ end
79
+
80
+ end
81
+
82
+ module CookiesMixin
83
+
84
+ def self.included(base)
85
+ # Allow per-controller default cookie domains (see callback below)
86
+ base.class_inheritable_accessor :_default_cookie_domain
87
+ base._default_cookie_domain = Merb::Config[:default_cookie_domain]
88
+
89
+ # Add a callback to enable Set-Cookie headers
90
+ base._after_dispatch_callbacks << lambda do |c|
91
+ headers = c.request.cookies.extract_headers("domain" => c._default_cookie_domain)
92
+ c.headers.update(headers)
93
+ end
94
+ end
95
+
96
+ # ==== Returns
97
+ # Merb::Cookies::
98
+ # A new Merb::Cookies instance representing the cookies that came in
99
+ # from the request object
100
+ #
101
+ # ==== Notes
102
+ # Headers are passed into the cookie object so that you can do:
103
+ # cookies[:foo] = "bar"
104
+ def cookies
105
+ request.cookies
106
+ end
107
+
108
+ module RequestMixin
109
+
110
+ # ==== Returns
111
+ # Hash:: The cookies for this request.
112
+ #
113
+ # ==== Notes
114
+ # If a method #default_cookies is defined it will be called. This can
115
+ # be used for session fixation purposes for example. The method returns
116
+ # a Hash of key => value pairs.
117
+ def cookies
118
+ @cookies ||= begin
119
+ values = self.class.query_parse(@env[Merb::Const::HTTP_COOKIE], ';,')
120
+ cookies = Merb::Cookies.new(values)
121
+ cookies.update(default_cookies) if respond_to?(:default_cookies)
122
+ cookies
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,93 @@
1
+ module Merb
2
+ class Dispatcher
3
+ module DefaultExceptionHelper
4
+ def humanize_exception(e)
5
+ e.class.name.split("::").last.gsub(/([a-z])([A-Z])/, '\1 \2')
6
+ end
7
+
8
+ def error_codes(exception)
9
+ if @show_details
10
+ message, message_details = exception.message.split("\n", 2)
11
+ "<h2>#{escape_html(message)}</h2><p>#{escape_html(message_details)}</p>"
12
+ else
13
+ "<h2>Sorry about that...</h2>"
14
+ end
15
+ end
16
+
17
+ def frame_details(line)
18
+ filename, lineno, location = line.split(":")
19
+ if filename.index(Merb.framework_root)
20
+ type = "framework"
21
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.framework_root))
22
+ elsif filename.index(Merb.root)
23
+ type = "app"
24
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(Merb.root))
25
+ elsif path = Gem.path.find {|p| filename.index(p)}
26
+ type = "gem"
27
+ shortname = Pathname.new(filename).relative_path_from(Pathname.new(path))
28
+ else
29
+ type = "other"
30
+ shortname = filename
31
+ end
32
+ [type, shortname, filename, lineno, location]
33
+ end
34
+
35
+ def listing(key, value, arr)
36
+ ret = []
37
+ ret << "<table class=\"listing\" style=\"display: none\">"
38
+ ret << " <thead>"
39
+ ret << " <tr><th width='25%'>#{key}</th><th width='75%'>#{value}</th></tr>"
40
+ ret << " </thead>"
41
+ ret << " <tbody>"
42
+ (arr || []).each_with_index do |(key, val), i|
43
+ klass = i % 2 == 0 ? "even" : "odd"
44
+ ret << " <tr class=#{klass}><td>#{key}</td><td>#{val.inspect}</td></tr>"
45
+ end
46
+ if arr.blank?
47
+ ret << " <tr class='odd'><td colspan='2'>None</td></tr>"
48
+ end
49
+ ret << " </tbody>"
50
+ ret << "</table>"
51
+ ret.join("\n")
52
+ end
53
+
54
+ def textmate_url(filename, line)
55
+ "<a href='txmt://open?url=file://#{filename}&amp;line=#{line}'>#{line}</a>"
56
+ end
57
+
58
+ def render_source(filename, line)
59
+ line = line.to_i
60
+ ret = []
61
+ ret << "<tr class='source'>"
62
+ ret << " <td class='collapse'></td>"
63
+ str = " <td class='code' colspan='2'><div>"
64
+
65
+ __caller_lines__(filename, line, 5) do |lline, lcode|
66
+ str << "<a href='txmt://open?url=file://#{filename}&amp;line=#{lline}'>#{lline}</a>"
67
+ str << "<em>" if line == lline
68
+ str << Erubis::XmlHelper.escape_xml(lcode)
69
+ str << "</em>" if line == lline
70
+ str << "\n"
71
+ end
72
+ str << "</div></td>"
73
+ ret << str
74
+ ret << "</tr>"
75
+ ret.join("\n")
76
+ end
77
+ end
78
+
79
+ class DefaultException < Merb::Controller
80
+ self._template_root = File.dirname(__FILE__) / "views"
81
+
82
+ def _template_location(context, type = nil, controller = controller_name)
83
+ "#{context}.#{type}"
84
+ end
85
+
86
+ def index
87
+ @exceptions = request.exceptions
88
+ @show_details = Merb::Config[:exception_details]
89
+ render :format => :html
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,198 @@
1
+ <style type="text/css" media="screen">
2
+ body {
3
+ font-family:arial;
4
+ font-size:11px;
5
+ }
6
+ p.options {
7
+ text-align: right;
8
+ }
9
+ p.options label {
10
+ margin-left: 10px;
11
+ }
12
+ p.options span.all {
13
+ padding: 5px;
14
+ border: 1px solid #000;
15
+ background-color: #fff;
16
+ }
17
+ p.options input {
18
+ position: relative;
19
+ }
20
+ h1 {
21
+ font-size:48px;
22
+ letter-spacing:-4px;
23
+ margin:0 0 20px 0;
24
+ line-height:36px;
25
+ color:#333;
26
+ }
27
+ h1 sup {
28
+ font-size: 0.5em;
29
+ }
30
+ h1 sup.error_500, h1 sup.error_400 {
31
+ color:#990E05;
32
+ }
33
+ h1 sup.error_100, h1 sup.error_200 {
34
+ color:#00BF10;
35
+ }
36
+ h1 sup.error_300 {
37
+ /* pretty sure you cant 'see' status 300
38
+ errors but if you could I think they
39
+ would be blue */
40
+ color:#1B2099;
41
+ }
42
+ h2 {
43
+ font-size:24px;
44
+ letter-spacing:-1px;
45
+ margin:0;
46
+ line-height:28px;
47
+ color:#444;
48
+ }
49
+ h3 {
50
+ cursor: pointer;
51
+ color: #006;
52
+ text-decoration: underline;
53
+ }
54
+ a, a:visited {
55
+ color:#00BF10;
56
+ }
57
+ .internalError {
58
+ width:800px;
59
+ margin:50px auto;
60
+ }
61
+ .header {
62
+ border-bottom:10px solid #333;
63
+ margin-bottom:1px;
64
+ background-image: url("data:image/gif;base64,R0lGODlhAwADAIAAAP///8zMzCH5BAAAAAAALAAAAAADAAMAAAIEBHIJBQA7");
65
+ padding:20px;
66
+ }
67
+ .header ul {
68
+ padding: 5px;
69
+ background-color: white;
70
+ }
71
+ .header ul li {
72
+ list-style-type: none;
73
+ font-size: 16px;
74
+ margin-bottom: 6px;
75
+ }
76
+
77
+ table.listing {
78
+ border-collapse: collapse;
79
+ font-size: 12px;
80
+ width: 100%;
81
+ }
82
+
83
+ table.listing th {
84
+ background-color: #000;
85
+ color: #fff;
86
+ }
87
+
88
+ table.listing td, table.listing th {
89
+ padding: 5px;
90
+ text-align: left;
91
+ vertical-align: top;
92
+ }
93
+
94
+ table.listing tr.odd {
95
+ background-color: #ccc;
96
+ }
97
+
98
+ table.listing tr.even {
99
+ background-color: #aaa;
100
+ }
101
+
102
+ table.listing td[colspan=2] {
103
+ text-align: center;
104
+ }
105
+
106
+ table.trace {
107
+ width:100%;
108
+ font-family:courier, monospace;
109
+ letter-spacing:-1px;
110
+ border-collapse: collapse;
111
+ border-spacing:0;
112
+ }
113
+ table.trace tr td{
114
+ padding:0;
115
+ height:26px;
116
+ font-size:13px;
117
+ vertical-align:middle;
118
+ }
119
+ table.trace tr.file{
120
+ border-top:2px solid #fff;
121
+ background-color:#F3F3F3;
122
+ }
123
+ table.trace tr.source {
124
+ background-color:#F8F8F8;
125
+ display:none;
126
+ }
127
+ table.trace .open tr.source {
128
+ display:table-row;
129
+ }
130
+ table.trace tr.file td.expand {
131
+ width:23px;
132
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAIAAABvSEP3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdVJREFUeNqMVL+TwUAYxaRIOlEhlZHGDAUzzOQ61+AqXMV1lJSU7q/QRqm8KFUcJTNn5qJkaPyoKKVz7y4mF8na5Kt29tt9+/Z97/u81+vVQ4r9frdarS6Xi7ETDIZisRjxMGPfmk4niNPpZE+xLAugbPaZ53nzvtfMBe/3+/3dbuehBrAKhZdUKkVAWa9Xsiybv0CPZDJZLr/qa5/BwgwRjYqOKIvFYjQa/aNommZh0Ww2K5UqzwfoQOPxaLPZ3FAmk0+7lplMpt1u53J5OpBOR0eZEE9wHJfP5zud93g88QhluwWbjW+5VOmKBgKBer3eaDTDYeGBQF8+x7rqIYoiPgixWJazpA6HA+MSxRArkUgMh0M409g8Ho8+9wYxxCqVSq1W26EDHGM2m4HOHQrEc38f/Yn7cLmlIRhBENzcx8cVRZnPZ/YUep2BWkjTIfA+PKVpZAXR5QxsjiqCKvGEqqp443w+0dvy17swqD0HB3S73V5PpkNg1qBqt8kwGCjmPkinM0QJbIoEa7U6UG6ToVgs4V9G2g0ESoP5Aoi7KYX5oCgf8IKbkvn9/mr1LRQKESamzgJy0g0tSZIuB3nuGqRU9Vv9C4sKkUhEkp4soxvxI8AAhWrrtXa3X8EAAAAASUVORK5CYII=);
133
+ background-position:top left;
134
+ background-repeat:no-repeat;
135
+ }
136
+ table.trace tr.file td.expand div {
137
+ width:23px;
138
+ }
139
+ table.trace .open tr.file td.expand {
140
+ width:23px;
141
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAB1CAIAAAAqdO2mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAXZJREFUeNrslK1ywkAUhcMOBomEOiSdqLxEBJX0NaijOsjyHGGmCGyQQYaiiiw4gktkcOmZbpsuuzQ/M5XnqJ2d3S/n3nM3rTzPLUP7/Tt0+pLcGQwG3W53OLyHzPMtjYL7q9UqSRLrD4E1Gj1orCvKYuFHUWTVkOM44/HjDcp8/lL4r6NerzeZPMm1KFw0QkDn83m5fP2lHA4fNQvRtNvtjsfDd0WzmSfb2e/fdTqdOvdh/HLJZLOn0+d2HJ+KRGzbdl23EpFlmed5cp2maRzHQq1lvQ5KMi6EUZBGfup6E1pTfd+vrGW7jbQ2C9hTt9BpqNyIWaAwAy6xg2eBz5iRC/NomiZhGN5sqmnkauo0BUGgVQoBjQ80oCACgNQdZHfTYBkF2mxCtWWAqunWpahxIDUt3QYUxIFQpJHyIWpXjinabKbbwItMHT+NyjchrP8QKaSQQgoppJBCCimkkEIKKaSQQgoppJBCCimkkEIKKaSo+hRgAEFD17X08O2NAAAAAElFTkSuQmCC);
142
+ background-position:top left;
143
+ background-repeat:no-repeat;
144
+ }
145
+ table.trace .open tr.file td.expand div {
146
+ width:23px;
147
+ }
148
+ table.trace tr.source td.collapse {
149
+ width:23px;
150
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAB1CAIAAAAqdO2mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVxJREFUeNrs0zFygkAUBmBlUkgJHdABlQwVkVJKKUxBYWbkALTxMJwhltyDFkss03IF8pudIcwaDaDl/6pd2P327b7d+eHwMXs4lNkzggoVKlSoUKFChQoVKlSoUKFChQoVKlSoUKFChQqVEYqm6ft9+qiSJEkYho7jTlcw2fd9NOI4nq4gEdFwXXe1Cqco63VkWVbXRTqLhTpOwQRpF7quR1E0TgGhqvLKUFCyoQqG/rks3O6kZKW/eRFpevOCoGTXVTcMQ5EyxyDEkML1c5RzuZOICIyXqn7JBVez6282MWrx731HOv2qB8Hri2lamNk0DfpVVdV1Peodappmmua8bdvzuc7zfNprzrLMth1FnGh/X8MjCAIQv/cFz/+65PcDh7rbvYv2ZUfdj+PxsyzLgVl0hKwgTqeqKApx2LeOc7t98zyv/1FWOgvx9RPii23bmL9cetJ8Ed8CDAC6aFW8bCzFhwAAAABJRU5ErkJggg==);
151
+ background-position:bottom left;
152
+ background-repeat:no-repeat;
153
+ background-color:#6F706F;
154
+ }
155
+ table.trace tr td.path {
156
+ padding-left:10px;
157
+ }
158
+ table.trace tbody.app td.path {
159
+ color: #900;
160
+ }
161
+ table.trace tbody.framework td.path {
162
+ color: #009;
163
+ }
164
+ table.trace tbody.gem td.path {
165
+ color: #090;
166
+ }
167
+ table.trace tr td.code {
168
+ padding: 10px 0 10px 20px;
169
+ white-space: pre;
170
+ }
171
+ table.trace tr td.code div {
172
+ width: 700px;
173
+ overflow-x: auto;
174
+ overflow-y: hidden;
175
+ }
176
+ table.trace tr td.code em {
177
+ font-weight:bold;
178
+ color:#00BF10;
179
+ }
180
+ table.trace tr td.code a {
181
+ width: 20px;
182
+ float: left;
183
+ }
184
+ table.trace tr td.code .more {
185
+ color:#666;
186
+ }
187
+ table.trace tr td.line {
188
+ width:30px;
189
+ text-align:right;
190
+ padding-right:4px;
191
+ }
192
+ .footer {
193
+ margin-top:5px;
194
+ font-size:11px;
195
+ color:#444;
196
+ text-align:right;
197
+ }
198
+ </style>
@@ -0,0 +1,73 @@
1
+ <script type="text/javascript" charset="utf-8">
2
+ (function() {
3
+ els = document.getElementsByTagName('td');
4
+ var forEach = function(arr, fn) {
5
+ for(var i=0; i<arr.length; i++) {
6
+ var res = fn(arr[i]);
7
+ if(res === false) break;
8
+ }
9
+ }
10
+ var toggleClasses = function(node, first, second) {
11
+ var classes = node.className.split(" ");
12
+ var newClasses = [];
13
+ forEach(classes, function(k) {
14
+ if(k == first) newClasses.push(second);
15
+ else if(k == second) newClasses.push(first);
16
+ else newClasses.push(k);
17
+ });
18
+ node.className = newClasses.join(" ");
19
+ }
20
+ forEach(els, function(el) {
21
+ // swap the open & closed classes
22
+ if(hasClass(el, "expand") || hasClass(el, "collapse")) {
23
+ el.onclick = function(e){
24
+ tbody = this.parentNode.parentNode;
25
+ toggleClasses(tbody, "open", "close");
26
+ }
27
+ }
28
+ })
29
+ forEach(document.getElementsByTagName("h3"), function(el) {
30
+ el.onclick = function(e) {
31
+ var tag = this.nextSibling;
32
+ while(tag.nodeType != 1) tag = tag.nextSibling;
33
+ tag.style.display = tag.style.display == "none" ? "" : "none";
34
+ }
35
+ })
36
+ function hasClass(node, matchClass) {
37
+ var classes = node.className.split(" ");
38
+ for(var i=0,className;className=classes[i];i++)
39
+ if(className == matchClass) return true;
40
+ return false;
41
+ }
42
+ var els = document.getElementsByTagName("p");
43
+ forEach(els, function(tag) {
44
+ if(tag.className != "options") return true;
45
+ var checkboxes = tag.getElementsByTagName("input");
46
+ forEach(checkboxes, function(box) {
47
+ if(window.navigator.userAgent.match(/Firefox/)) {
48
+ box.style.top = "3px";
49
+ }
50
+ });
51
+ tag.getElementsByTagName("input")[0].onclick = function(e) {
52
+ forEach(checkboxes, function(box) {
53
+ if(box == e.target) return true;
54
+ box.checked = e.target.checked;
55
+ toggleTraces(box, box);
56
+ })
57
+ };
58
+ var toggleTraces = function(box, target) {
59
+ var tbodies = tag.parentNode.getElementsByTagName("tbody");
60
+ forEach(tbodies, function(tbody) {
61
+ if(hasClass(tbody, target.parentNode.className)) {
62
+ if(target.checked) tbody.style.display = "";
63
+ else tbody.style.display = "none";
64
+ }
65
+ })
66
+ }
67
+ forEach(checkboxes, function(box) {
68
+ if(box == checkboxes[0]) return true;
69
+ box.onchange = function(e) { toggleTraces(box, e.target) }
70
+ })
71
+ })
72
+ })();
73
+ </script>