wycats-merb-core 0.9.8 → 0.9.9
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.
- data/CHANGELOG +136 -2
- data/CONTRIBUTORS +6 -0
- data/PUBLIC_CHANGELOG +15 -0
- data/Rakefile +12 -14
- data/lib/merb-core.rb +82 -43
- data/lib/merb-core/bootloader.rb +268 -60
- data/lib/merb-core/config.rb +119 -34
- data/lib/merb-core/controller/abstract_controller.rb +58 -18
- data/lib/merb-core/controller/exceptions.rb +2 -15
- data/lib/merb-core/controller/merb_controller.rb +28 -1
- data/lib/merb-core/controller/mime.rb +4 -0
- data/lib/merb-core/controller/mixins/controller.rb +14 -17
- data/lib/merb-core/controller/mixins/render.rb +23 -28
- data/lib/merb-core/controller/mixins/responder.rb +0 -1
- data/lib/merb-core/controller/template.rb +44 -20
- data/lib/merb-core/core_ext/kernel.rb +8 -3
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +1 -1
- data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +3 -1
- data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +71 -67
- data/lib/merb-core/dispatch/default_exception/views/index.html.erb +6 -2
- data/lib/merb-core/dispatch/dispatcher.rb +5 -9
- data/lib/merb-core/dispatch/request.rb +46 -57
- data/lib/merb-core/dispatch/router.rb +83 -6
- data/lib/merb-core/dispatch/router/behavior.rb +87 -27
- data/lib/merb-core/dispatch/router/resources.rb +281 -167
- data/lib/merb-core/dispatch/router/route.rb +141 -27
- data/lib/merb-core/logger.rb +213 -202
- data/lib/merb-core/rack.rb +3 -1
- data/lib/merb-core/rack/adapter.rb +7 -4
- data/lib/merb-core/rack/adapter/ebb.rb +12 -13
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -15
- data/lib/merb-core/rack/adapter/irb.rb +3 -2
- data/lib/merb-core/rack/adapter/mongrel.rb +22 -15
- data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +4 -16
- data/lib/merb-core/rack/adapter/thin.rb +21 -22
- data/lib/merb-core/rack/adapter/thin_turbo.rb +4 -11
- data/lib/merb-core/rack/adapter/webrick.rb +54 -18
- data/lib/merb-core/rack/handler/mongrel.rb +12 -13
- data/lib/merb-core/rack/middleware/csrf.rb +1 -1
- data/lib/merb-core/server.rb +135 -98
- data/lib/merb-core/tasks/gem_management.rb +50 -12
- data/lib/merb-core/tasks/merb.rb +1 -0
- data/lib/merb-core/tasks/merb_rake_helper.rb +9 -38
- data/lib/merb-core/tasks/stats.rake +2 -2
- data/lib/merb-core/test.rb +9 -3
- data/lib/merb-core/test/helpers.rb +1 -0
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -2
- data/lib/merb-core/test/helpers/request_helper.rb +40 -372
- data/lib/merb-core/test/helpers/route_helper.rb +15 -7
- data/lib/merb-core/test/matchers.rb +1 -0
- data/lib/merb-core/test/matchers/controller_matchers.rb +4 -247
- data/lib/merb-core/test/matchers/view_matchers.rb +22 -4
- data/lib/merb-core/test/run_specs.rb +117 -25
- data/lib/merb-core/version.rb +1 -1
- metadata +1 -1
- data/lib/merb-core/vendor/facets.rb +0 -2
- data/lib/merb-core/vendor/facets/dictionary.rb +0 -433
- data/lib/merb-core/vendor/facets/inflect.rb +0 -342
@@ -7,6 +7,10 @@ module Merb
|
|
7
7
|
ResponderMixin::TYPES
|
8
8
|
end
|
9
9
|
|
10
|
+
def available_accepts
|
11
|
+
ResponderMixin::MIMES
|
12
|
+
end
|
13
|
+
|
10
14
|
# Any specific outgoing headers should be included here. These are not
|
11
15
|
# the content-type header but anything in addition to it.
|
12
16
|
# +transform_method+ should be set to a symbol of the method used to
|
@@ -62,6 +62,7 @@ module Merb
|
|
62
62
|
# ==== Parameters
|
63
63
|
# data<String>:: a chunk of data to return.
|
64
64
|
def send_chunk(data)
|
65
|
+
only_runs_on_mongrel!
|
65
66
|
@response.write('%x' % data.size + "\r\n")
|
66
67
|
@response.write(data + "\r\n")
|
67
68
|
end
|
@@ -76,12 +77,8 @@ module Merb
|
|
76
77
|
# A block that Mongrel can call later, allowing Merb to release the
|
77
78
|
# thread lock and render another request.
|
78
79
|
def render_deferred(&blk)
|
79
|
-
must_support_streaming!
|
80
80
|
Proc.new {|response|
|
81
|
-
|
82
|
-
response.send_status(result.length)
|
83
|
-
response.send_header
|
84
|
-
response.write(result)
|
81
|
+
response.write(blk.call)
|
85
82
|
}
|
86
83
|
end
|
87
84
|
|
@@ -96,10 +93,7 @@ module Merb
|
|
96
93
|
# Proc::
|
97
94
|
# A block that Mongrel can call after returning the string to the user.
|
98
95
|
def render_then_call(str, &blk)
|
99
|
-
must_support_streaming!
|
100
96
|
Proc.new {|response|
|
101
|
-
response.send_status(str.length)
|
102
|
-
response.send_header
|
103
97
|
response.write(str)
|
104
98
|
blk.call
|
105
99
|
}
|
@@ -166,7 +160,13 @@ module Merb
|
|
166
160
|
'Content-Disposition' => disposition,
|
167
161
|
'Content-Transfer-Encoding' => 'binary'
|
168
162
|
)
|
169
|
-
|
163
|
+
Proc.new {|response|
|
164
|
+
file = File.open(file, 'rb')
|
165
|
+
while chunk = file.read(16384)
|
166
|
+
response.write chunk
|
167
|
+
end
|
168
|
+
file.close
|
169
|
+
}
|
170
170
|
end
|
171
171
|
|
172
172
|
# Send binary data over HTTP to the user as a file download. May set content type,
|
@@ -217,7 +217,6 @@ module Merb
|
|
217
217
|
# end
|
218
218
|
# end
|
219
219
|
def stream_file(opts={}, &stream)
|
220
|
-
must_support_streaming!
|
221
220
|
opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
|
222
221
|
disposition = opts[:disposition].dup || 'attachment'
|
223
222
|
disposition << %(; filename="#{opts[:filename]}")
|
@@ -229,8 +228,6 @@ module Merb
|
|
229
228
|
'CONTENT-LENGTH' => opts[:content_length].to_s
|
230
229
|
)
|
231
230
|
Proc.new{|response|
|
232
|
-
response.send_status(opts[:content_length])
|
233
|
-
response.send_header
|
234
231
|
stream.call(response)
|
235
232
|
}
|
236
233
|
end
|
@@ -306,13 +303,13 @@ module Merb
|
|
306
303
|
alias escape_html escape_xml
|
307
304
|
|
308
305
|
private
|
309
|
-
#
|
306
|
+
# Marks an output method that only runs on the mongrel webserver.
|
310
307
|
#
|
311
308
|
# ==== Raises
|
312
|
-
# NotImplemented:: The Rack adapter
|
313
|
-
def
|
314
|
-
unless
|
315
|
-
raise(Merb::ControllerExceptions::NotImplemented, "Current Rack adapter
|
309
|
+
# NotImplemented:: The Rack adapter is not mongrel.
|
310
|
+
def only_runs_on_mongrel!
|
311
|
+
unless Merb::Config[:log_stream] == 'mongrel'
|
312
|
+
raise(Merb::ControllerExceptions::NotImplemented, "Current Rack adapter is not mongrel. cannot support this feature")
|
316
313
|
end
|
317
314
|
end
|
318
315
|
end
|
@@ -268,17 +268,6 @@ module Merb::RenderMixin
|
|
268
268
|
#
|
269
269
|
# In this case, "one" will be available in the partial through the local
|
270
270
|
# variable named +number+.
|
271
|
-
#
|
272
|
-
# ==== Notes
|
273
|
-
# It is important to note that the object being passed to the partial
|
274
|
-
# as well as any extra local variables cannot use names of helper methods
|
275
|
-
# since any helper method of the same name will take precedence over the
|
276
|
-
# passed variable. Example:
|
277
|
-
#
|
278
|
-
# partial :bar, :with => "one", :as => :partial
|
279
|
-
#
|
280
|
-
# In this case, "one" will not be available in the partial because "partial"
|
281
|
-
# is already a helper method.
|
282
271
|
def partial(template, opts={})
|
283
272
|
|
284
273
|
# partial :foo becomes "#{controller_name}/_foo"
|
@@ -290,32 +279,36 @@ module Merb::RenderMixin
|
|
290
279
|
kontroller = (m = template.match(/.*(?=\/)/)) ? m[0] : controller_name
|
291
280
|
template = "_#{File.basename(template)}"
|
292
281
|
end
|
293
|
-
template_method, template_location =
|
294
|
-
_template_for(template, opts.delete(:format) || content_type, kontroller, template_path)
|
295
282
|
|
296
|
-
(@_old_partial_locals ||= []).push @_merb_partial_locals
|
297
|
-
|
298
283
|
# This handles no :with as well
|
299
284
|
with = [opts.delete(:with)].flatten
|
300
|
-
as = opts.delete(:as) ||
|
301
|
-
|
302
|
-
|
285
|
+
as = (opts.delete(:as) || template.match(%r[(?:.*/)?_([^\./]*)])[1]).to_sym
|
286
|
+
|
287
|
+
# Ensure that as is in the locals hash even if it isn't passed in here
|
288
|
+
# so that it's included in the preamble.
|
289
|
+
locals = opts.merge(:collection_index => -1, :collection_size => with.size, as => opts[as])
|
290
|
+
template_method, template_location = _template_for(
|
291
|
+
template,
|
292
|
+
opts.delete(:format) || content_type,
|
293
|
+
kontroller,
|
294
|
+
template_path,
|
295
|
+
locals.keys)
|
303
296
|
|
304
297
|
# this handles an edge-case where the name of the partial is _foo.* and your opts
|
305
298
|
# have :foo as a key.
|
306
|
-
named_local =
|
299
|
+
named_local = opts.key?(as)
|
307
300
|
|
308
301
|
sent_template = with.map do |temp|
|
309
|
-
|
302
|
+
locals[as] = temp unless named_local
|
303
|
+
|
310
304
|
if template_method && self.respond_to?(template_method)
|
311
|
-
|
312
|
-
send(template_method)
|
305
|
+
locals[:collection_index] += 1
|
306
|
+
send(template_method, locals)
|
313
307
|
else
|
314
308
|
raise TemplateNotFound, "Could not find template at #{template_location}.*"
|
315
309
|
end
|
316
310
|
end.join
|
317
311
|
|
318
|
-
@_merb_partial_locals = @_old_partial_locals.pop
|
319
312
|
sent_template
|
320
313
|
end
|
321
314
|
|
@@ -382,6 +375,7 @@ module Merb::RenderMixin
|
|
382
375
|
# context<Object>:: The controller action or template (basename or absolute path).
|
383
376
|
# content_type<~to_s>:: The content type (like html or json).
|
384
377
|
# controller<~to_s>:: The name of the controller. Defaults to nil.
|
378
|
+
# locals<Array[Symbol]>:: A list of locals to assign from the args passed into the compiled template.
|
385
379
|
#
|
386
380
|
# ==== Options (opts)
|
387
381
|
# :template<String>::
|
@@ -391,13 +385,13 @@ module Merb::RenderMixin
|
|
391
385
|
# ==== Returns
|
392
386
|
# Array[Symbol, String]::
|
393
387
|
# A pair consisting of the template method and location.
|
394
|
-
def _template_for(context, content_type, controller=nil, template=nil)
|
388
|
+
def _template_for(context, content_type, controller=nil, template=nil, locals=[])
|
395
389
|
template_method, template_location = nil, nil
|
396
390
|
|
397
391
|
# absolute path to a template (:template => "/foo/bar")
|
398
392
|
if template.is_a?(String) && template =~ %r{^/}
|
399
393
|
template_location = self._absolute_template_location(template, content_type)
|
400
|
-
return [_template_method_for(template_location), template_location]
|
394
|
+
return [_template_method_for(template_location, locals), template_location]
|
401
395
|
end
|
402
396
|
|
403
397
|
self.class._template_roots.reverse_each do |root, template_meth|
|
@@ -409,7 +403,7 @@ module Merb::RenderMixin
|
|
409
403
|
template_location = root / self.send(template_meth, context, content_type, controller)
|
410
404
|
end
|
411
405
|
|
412
|
-
break if template_method = _template_method_for(template_location.to_s)
|
406
|
+
break if template_method = _template_method_for(template_location.to_s, locals)
|
413
407
|
end
|
414
408
|
|
415
409
|
# template_location is a Pathname
|
@@ -421,11 +415,12 @@ module Merb::RenderMixin
|
|
421
415
|
#
|
422
416
|
# ==== Parameters
|
423
417
|
# template_location<String>:: The phyical path of the template
|
418
|
+
# locals<Array[Symbol]>:: A list of locals to assign from the args passed into the compiled template.
|
424
419
|
#
|
425
420
|
# ==== Returns
|
426
421
|
# String:: The method, if it exists. Otherwise return nil.
|
427
|
-
def _template_method_for(template_location)
|
428
|
-
meth = Merb::Template.template_for(template_location)
|
422
|
+
def _template_method_for(template_location, locals)
|
423
|
+
meth = Merb::Template.template_for(template_location, [], locals)
|
429
424
|
meth && self.respond_to?(meth) ? meth : nil
|
430
425
|
end
|
431
426
|
|
@@ -3,9 +3,10 @@ end
|
|
3
3
|
|
4
4
|
module Merb::Template
|
5
5
|
|
6
|
-
EXTENSIONS
|
7
|
-
METHOD_LIST
|
8
|
-
|
6
|
+
EXTENSIONS = {} unless defined?(EXTENSIONS)
|
7
|
+
METHOD_LIST = {} unless defined?(METHOD_LIST)
|
8
|
+
SUPPORTED_LOCALS_LIST = Hash.new([].freeze) unless defined?(SUPPORTED_LOCALS_LIST)
|
9
|
+
MTIMES = {} unless defined?(MTIMES)
|
9
10
|
|
10
11
|
class << self
|
11
12
|
# Get the template's method name from a full path. This replaces
|
@@ -55,26 +56,37 @@ module Merb::Template
|
|
55
56
|
# ==== Parameters
|
56
57
|
# path<String>:: A full path to find a template method for.
|
57
58
|
# template_stack<Array>:: The template stack. Not used.
|
59
|
+
# locals<Array[Symbol]>:: The names of local variables
|
58
60
|
#
|
59
61
|
# ==== Returns
|
60
62
|
# <String>:: name of the method that inlines the template.
|
61
63
|
#---
|
62
64
|
# @semipublic
|
63
|
-
def template_for(path, template_stack = [])
|
65
|
+
def template_for(path, template_stack = [], locals=[])
|
64
66
|
path = File.expand_path(path)
|
65
67
|
|
66
|
-
|
67
|
-
if Merb::Config[:reload_templates]
|
68
|
+
if needs_compilation?(path, locals)
|
68
69
|
file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
|
69
|
-
METHOD_LIST[path] = file ? inline_template(load_template_io(file)) : nil
|
70
|
-
else
|
71
|
-
METHOD_LIST[path] ||= begin
|
72
|
-
file = Dir["#{path}.{#{template_extensions.join(',')}}"].first
|
73
|
-
file ? inline_template(load_template_io(file)) : nil
|
74
|
-
end
|
70
|
+
METHOD_LIST[path] = file ? inline_template(load_template_io(file), locals) : nil
|
75
71
|
end
|
76
72
|
|
77
|
-
|
73
|
+
METHOD_LIST[path]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Decide if a template needs to be re/compiled.
|
77
|
+
#
|
78
|
+
# ==== Parameters
|
79
|
+
# path<String>:: The full path of the template to check support for.
|
80
|
+
# locals<Array[Symbol]>:: The list of locals that need to be supported
|
81
|
+
#
|
82
|
+
# ==== Returns
|
83
|
+
# Boolean:: Whether or not the template for the provided path needs to be recompiled
|
84
|
+
#---
|
85
|
+
def needs_compilation?(path, locals)
|
86
|
+
return true if Merb::Config[:reload_templates] || !METHOD_LIST[path]
|
87
|
+
|
88
|
+
current_locals = SUPPORTED_LOCALS_LIST[path]
|
89
|
+
locals.any?{|local| !current_locals.include?(local)}
|
78
90
|
end
|
79
91
|
|
80
92
|
# Get all known template extensions
|
@@ -93,6 +105,9 @@ module Merb::Template
|
|
93
105
|
# ==== Parameters
|
94
106
|
# io<#path>::
|
95
107
|
# An IO that responds to #path (File or VirtualFile)
|
108
|
+
# locals<Array[Symbol]>::
|
109
|
+
# A list of local names that should be assigned in the template method
|
110
|
+
# from the arguments hash. Defaults to [].
|
96
111
|
# mod<Module>::
|
97
112
|
# The module to put the compiled method into. Defaults to
|
98
113
|
# Merb::InlineTemplates
|
@@ -102,10 +117,14 @@ module Merb::Template
|
|
102
117
|
# must be available to instances of AbstractController that will use it.
|
103
118
|
#---
|
104
119
|
# @public
|
105
|
-
def inline_template(io, mod = Merb::InlineTemplates)
|
106
|
-
|
107
|
-
|
108
|
-
|
120
|
+
def inline_template(io, locals=[], mod = Merb::InlineTemplates)
|
121
|
+
full_file_path = File.expand_path(io.path)
|
122
|
+
engine_neutral_path = full_file_path.gsub(/\.[^\.]*$/, "")
|
123
|
+
|
124
|
+
SUPPORTED_LOCALS_LIST[engine_neutral_path] |= locals unless locals.empty?
|
125
|
+
ret = METHOD_LIST[engine_neutral_path] =
|
126
|
+
engine_for(full_file_path).compile_template(io, template_name(full_file_path), locals, mod)
|
127
|
+
|
109
128
|
io.close
|
110
129
|
ret
|
111
130
|
end
|
@@ -156,12 +175,17 @@ module Merb::Template
|
|
156
175
|
# ==== Parameters
|
157
176
|
# io<#path>:: An IO containing the full path of the template.
|
158
177
|
# name<String>:: The name of the method that will be created.
|
178
|
+
# locals<Array[Symbol]>:: A list of locals to assign from the args passed into the compiled template.
|
159
179
|
# mod<Module>:: The module that the compiled method will be placed into.
|
160
|
-
def self.compile_template(io, name, mod)
|
180
|
+
def self.compile_template(io, name, locals, mod)
|
161
181
|
template = ::Erubis::BlockAwareEruby.new(io.read)
|
162
|
-
|
163
182
|
_old_verbose, $VERBOSE = $VERBOSE, nil
|
164
|
-
|
183
|
+
assigns = locals.inject([]) do |assigns, local|
|
184
|
+
assigns << "#{local} = _locals[#{local.inspect}]"
|
185
|
+
end.join(";")
|
186
|
+
|
187
|
+
code = "def #{name}(_locals={}); #{assigns}; #{template.src}; end"
|
188
|
+
mod.module_eval code, File.expand_path(io.path)
|
165
189
|
$VERBOSE = _old_verbose
|
166
190
|
|
167
191
|
name
|
@@ -34,10 +34,13 @@ module Kernel
|
|
34
34
|
# @param name<String> The name of the gem to load.
|
35
35
|
# @param *ver<Gem::Requirement, Gem::Version, Array, #to_str>
|
36
36
|
# Version requirements to be passed to Gem::Dependency.new.
|
37
|
+
# If the last argument is a Hash, extract the :immediate option,
|
38
|
+
# forcing a dependency to load immediately.
|
37
39
|
#
|
38
40
|
# @return <Gem::Dependency> The dependency information.
|
39
41
|
def dependency(name, *ver)
|
40
|
-
|
42
|
+
immediate = ver.last.is_a?(Hash) && ver.pop[:immediate]
|
43
|
+
if immediate || Merb::BootLoader.finished?(Merb::BootLoader::Dependencies)
|
41
44
|
load_dependency(name, *ver)
|
42
45
|
else
|
43
46
|
track_dependency(name, *ver)
|
@@ -230,9 +233,11 @@ module Kernel
|
|
230
233
|
else
|
231
234
|
lines = File.read(file).split("\n")
|
232
235
|
first_line = (f = line - size - 1) < 0 ? 0 : f
|
233
|
-
lines = lines[first_line, size * 2 + 1]
|
234
236
|
|
235
|
-
|
237
|
+
old_lines = lines
|
238
|
+
lines = lines[first_line, size * 2 + 1]
|
239
|
+
|
240
|
+
lines && lines.each_with_index do |str, index|
|
236
241
|
yield index + line - size, str.chomp
|
237
242
|
end
|
238
243
|
end
|
@@ -41,7 +41,7 @@ module Merb
|
|
41
41
|
ret << " <tbody>"
|
42
42
|
(arr || []).each_with_index do |(key, val), i|
|
43
43
|
klass = i % 2 == 0 ? "even" : "odd"
|
44
|
-
ret << " <tr class
|
44
|
+
ret << " <tr class=\"#{klass}\"><td>#{key}</td><td>#{val.inspect}</td></tr>"
|
45
45
|
end
|
46
46
|
if arr.blank?
|
47
47
|
ret << " <tr class='odd'><td colspan='2'>None</td></tr>"
|
@@ -1,73 +1,77 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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";
|
1
|
+
<% if Merb.env != "test" %>
|
2
|
+
<script type="text/javascript">
|
3
|
+
//<![CDATA[
|
4
|
+
(function() {
|
5
|
+
els = document.getElementsByTagName('td');
|
6
|
+
var forEach = function(arr, fn) {
|
7
|
+
for(var i=0; i<arr.length; i++) {
|
8
|
+
var res = fn(arr[i]);
|
9
|
+
if(res === false) break;
|
49
10
|
}
|
50
|
-
}
|
51
|
-
|
11
|
+
}
|
12
|
+
var toggleClasses = function(node, first, second) {
|
13
|
+
var classes = node.className.split(" ");
|
14
|
+
var newClasses = [];
|
15
|
+
forEach(classes, function(k) {
|
16
|
+
if(k == first) newClasses.push(second);
|
17
|
+
else if(k == second) newClasses.push(first);
|
18
|
+
else newClasses.push(k);
|
19
|
+
});
|
20
|
+
node.className = newClasses.join(" ");
|
21
|
+
}
|
22
|
+
forEach(els, function(el) {
|
23
|
+
// swap the open & closed classes
|
24
|
+
if(hasClass(el, "expand") || hasClass(el, "collapse")) {
|
25
|
+
el.onclick = function(e){
|
26
|
+
tbody = this.parentNode.parentNode;
|
27
|
+
toggleClasses(tbody, "open", "close");
|
28
|
+
}
|
29
|
+
}
|
30
|
+
})
|
31
|
+
forEach(document.getElementsByTagName("h3"), function(el) {
|
32
|
+
el.onclick = function(e) {
|
33
|
+
var tag = this.nextSibling;
|
34
|
+
while(tag.nodeType != 1) tag = tag.nextSibling;
|
35
|
+
tag.style.display = tag.style.display == "none" ? "" : "none";
|
36
|
+
}
|
37
|
+
})
|
38
|
+
function hasClass(node, matchClass) {
|
39
|
+
var classes = node.className.split(" ");
|
40
|
+
for(var i=0,className;className=classes[i];i++)
|
41
|
+
if(className == matchClass) return true;
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
var els = document.getElementsByTagName("p");
|
45
|
+
forEach(els, function(tag) {
|
46
|
+
if(tag.className != "options") return true;
|
47
|
+
var checkboxes = tag.getElementsByTagName("input");
|
52
48
|
forEach(checkboxes, function(box) {
|
53
|
-
if(
|
54
|
-
|
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";
|
49
|
+
if(window.navigator.userAgent.match(/Firefox/)) {
|
50
|
+
box.style.top = "3px";
|
64
51
|
}
|
52
|
+
});
|
53
|
+
tag.getElementsByTagName("input")[0].onclick = function(e) {
|
54
|
+
forEach(checkboxes, function(box) {
|
55
|
+
if(box == e.target) return true;
|
56
|
+
box.checked = e.target.checked;
|
57
|
+
toggleTraces(box, box);
|
58
|
+
})
|
59
|
+
};
|
60
|
+
var toggleTraces = function(box, target) {
|
61
|
+
var tbodies = tag.parentNode.getElementsByTagName("tbody");
|
62
|
+
forEach(tbodies, function(tbody) {
|
63
|
+
if(hasClass(tbody, target.parentNode.className)) {
|
64
|
+
if(target.checked) tbody.style.display = "";
|
65
|
+
else tbody.style.display = "none";
|
66
|
+
}
|
67
|
+
})
|
68
|
+
}
|
69
|
+
forEach(checkboxes, function(box) {
|
70
|
+
if(box == checkboxes[0]) return true;
|
71
|
+
box.onchange = function(e) { toggleTraces(box, e.target) }
|
65
72
|
})
|
66
|
-
}
|
67
|
-
forEach(checkboxes, function(box) {
|
68
|
-
if(box == checkboxes[0]) return true;
|
69
|
-
box.onchange = function(e) { toggleTraces(box, e.target) }
|
70
73
|
})
|
71
|
-
})
|
72
|
-
|
74
|
+
})();
|
75
|
+
//]]>
|
73
76
|
</script>
|
77
|
+
<% end %>
|