cuke_sniffer 0.0.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/cuke_sniffer +4 -1
- data/lib/cuke_sniffer/cli.rb +15 -4
- data/lib/cuke_sniffer/cuke_sniffer_helper.rb +4 -3
- data/lib/cuke_sniffer/dead_steps_helper.rb +53 -53
- data/lib/cuke_sniffer/formatter.rb +166 -125
- data/lib/cuke_sniffer/js/cuke_sniffer.js +184 -0
- data/lib/cuke_sniffer/report/css.html.erb +122 -132
- data/lib/cuke_sniffer/report/dead_steps.html.erb +61 -61
- data/lib/cuke_sniffer/report/dead_steps_min.html.erb +23 -0
- data/lib/cuke_sniffer/report/expand_and_collapse_buttons.html.erb +8 -0
- data/lib/cuke_sniffer/report/features.html.erb +124 -106
- data/lib/cuke_sniffer/report/hooks.html.erb +76 -76
- data/lib/cuke_sniffer/report/improvement_list.html.erb +34 -24
- data/lib/cuke_sniffer/report/information.html.erb +81 -0
- data/lib/cuke_sniffer/report/js.html.erb +9 -65
- data/lib/cuke_sniffer/report/min_template.html.erb +12 -7
- data/lib/cuke_sniffer/report/rules.html.erb +100 -68
- data/lib/cuke_sniffer/report/standard_template.html.erb +16 -12
- data/lib/cuke_sniffer/report/step_definitions.html.erb +69 -63
- data/lib/cuke_sniffer/report/summary.html.erb +131 -96
- data/lib/cuke_sniffer/report/title.html.erb +13 -9
- data/lib/cuke_sniffer/rule.rb +1 -0
- data/lib/cuke_sniffer/rule_config.rb +2 -2
- data/lib/cuke_sniffer/rules_evaluator.rb +1 -2
- data/lib/cuke_sniffer/scenario.rb +67 -23
- data/lib/cuke_sniffer/summary_helper.rb +90 -90
- metadata +31 -46
- data/lib/cuke_sniffer/report/legend.html.erb +0 -167
@@ -0,0 +1,184 @@
|
|
1
|
+
var CukeSniffer = {
|
2
|
+
init: function(){
|
3
|
+
CukeSniffer.actions.bind();
|
4
|
+
CukeSniffer.view._startTooltips();
|
5
|
+
CukeSniffer.view._updateRowColors();
|
6
|
+
CukeSniffer.view._disableRulesFromLocalStorage();
|
7
|
+
},
|
8
|
+
view: {
|
9
|
+
_disableRulesFromLocalStorage: function(){
|
10
|
+
$.each($("[data-rule-symbol]"), function(index, rule){
|
11
|
+
var $rule = $(rule),
|
12
|
+
symbol = $rule.data("rule-symbol"),
|
13
|
+
hasCookie = localStorage.getItem(symbol) != undefined;
|
14
|
+
if(hasCookie){
|
15
|
+
$rule.find("input").click();
|
16
|
+
}
|
17
|
+
});
|
18
|
+
},
|
19
|
+
toggleDetails: function(){
|
20
|
+
$(this).closest(".deadStep, .feature, .stepDefinition, .hook").find(".row > .details").slideToggle();
|
21
|
+
},
|
22
|
+
_startTooltips: function(){
|
23
|
+
$("[title]").tooltip();
|
24
|
+
},
|
25
|
+
_updateRowColors: function(){
|
26
|
+
$(".rule:visible:even, .deadStep:even, .feature:even, .stepDefinition:even, .hook:even").addClass("blueRow");
|
27
|
+
},
|
28
|
+
_updateVisibleItems: function(){
|
29
|
+
$(".feature, .backgroundProblems, .scenario, .scenarios, .stepDefinition, .hook").hide();
|
30
|
+
CukeSniffer.view._updateVisibleFeatureIssues();
|
31
|
+
CukeSniffer.view._updateVisibleStepDefinitionIssues();
|
32
|
+
CukeSniffer.view._updateVisibleHookIssues();
|
33
|
+
},
|
34
|
+
_updateVisibleFeatureIssues: function(){
|
35
|
+
var $visibleFeatures = $(".feature:has([show])"),
|
36
|
+
$visibleBackground = $(".backgroundProblems:has([show])"),
|
37
|
+
$visibleScenarioBlocks = $(".scenarios:has([show])"),
|
38
|
+
$visibleScenarios = $(".scenario:has(.details [data-improvement-row][show])");
|
39
|
+
|
40
|
+
$visibleFeatures.show();
|
41
|
+
$visibleBackground.show();
|
42
|
+
$visibleScenarioBlocks.show();
|
43
|
+
$visibleScenarios.show();
|
44
|
+
$visibleScenarios.find(".details").show();
|
45
|
+
},
|
46
|
+
_updateVisibleStepDefinitionIssues: function(){
|
47
|
+
var $visibleStepDefinition = $(".stepDefinition:has([show])")
|
48
|
+
$visibleStepDefinition.show();
|
49
|
+
},
|
50
|
+
_updateVisibleHookIssues: function(){
|
51
|
+
var $visibleHooks = $(".hook:has([show])")
|
52
|
+
$visibleHooks.show();
|
53
|
+
}
|
54
|
+
},
|
55
|
+
actions: {
|
56
|
+
bind: function(){
|
57
|
+
CukeSniffer.actions._bindExpandAll();
|
58
|
+
CukeSniffer.actions._bindCollapseAll();
|
59
|
+
CukeSniffer.actions._bindShowDeadSteps()
|
60
|
+
CukeSniffer.actions._togglePanelHeaderIcons();
|
61
|
+
CukeSniffer.actions._viewRules();
|
62
|
+
CukeSniffer.actions._clickRow();
|
63
|
+
CukeSniffer.actions._ruleFilters();
|
64
|
+
CukeSniffer.actions.toggleDetails();
|
65
|
+
CukeSniffer.actions.enableAllRules();
|
66
|
+
CukeSniffer.actions.disableAllRules();
|
67
|
+
CukeSniffer.actions.changeRuleStatus();
|
68
|
+
},
|
69
|
+
_bindExpandAll: function(){
|
70
|
+
$(document).on("click", "[expand]", function(){
|
71
|
+
$(this).closest(".panel-body").find(".details").show();
|
72
|
+
});
|
73
|
+
},
|
74
|
+
_bindShowDeadSteps: function(){
|
75
|
+
$("#step_definitions .deadStep").hide();
|
76
|
+
$(document).on("click", "#showDeadSteps", function(){
|
77
|
+
$("#step_definitions .deadStep").toggle()
|
78
|
+
});
|
79
|
+
},
|
80
|
+
_bindCollapseAll: function(){
|
81
|
+
$(document).on("click", "[collapse]", function(){
|
82
|
+
$(this).closest(".panel-body").find(".details").hide();
|
83
|
+
});
|
84
|
+
},
|
85
|
+
_togglePanelHeaderIcons: function(){
|
86
|
+
$(document).on("click", ".panel-title", function(){
|
87
|
+
var $icon = $(this).find(".glyphicon");
|
88
|
+
$icon.toggleClass("glyphicon-menu-down");
|
89
|
+
$icon.toggleClass("glyphicon-menu-up");
|
90
|
+
});
|
91
|
+
},
|
92
|
+
_viewRules: function(){
|
93
|
+
$(document).on("click", "#rulesTab", CukeSniffer.view._updateRowColors);
|
94
|
+
},
|
95
|
+
_clickRow: function(){
|
96
|
+
$(document).on("click", ".rule", function(){
|
97
|
+
var $details = $(this).find(".details");
|
98
|
+
$details.toggle();
|
99
|
+
});
|
100
|
+
$(document).on("click", ".rule :checkbox", function(event){
|
101
|
+
event.stopPropagation();
|
102
|
+
});
|
103
|
+
},
|
104
|
+
_ruleFilters: function(){
|
105
|
+
$(document).on("click", "#ruleFilters .btn", function(){
|
106
|
+
var $clickedCheckbox = $(this).find(":checkbox");
|
107
|
+
$(".rule").hide().removeClass("blueRow");
|
108
|
+
$("#ruleFilters .btn").each(function(index, button){
|
109
|
+
var $checkbox = $(button).find(":checkbox"),
|
110
|
+
filterName = $checkbox.data("rule-type"),
|
111
|
+
//hate this hack.
|
112
|
+
checked = ($clickedCheckbox[0] == $checkbox[0]) ? !$checkbox.is(":checked") : $checkbox.is(":checked");
|
113
|
+
if(checked){
|
114
|
+
$("[rule-" + filterName + "]").show();
|
115
|
+
}
|
116
|
+
});
|
117
|
+
CukeSniffer.view._updateRowColors();
|
118
|
+
});
|
119
|
+
},
|
120
|
+
toggleDetails: function(){
|
121
|
+
$(document).on("click", ".deadStep > .row > .title," +
|
122
|
+
" .feature > .row > .title, " +
|
123
|
+
".stepDefinition > .row > .title, " +
|
124
|
+
".hook .title", CukeSniffer.view.toggleDetails);
|
125
|
+
},
|
126
|
+
enableAllRules: function(){
|
127
|
+
$(document).on("click", "#enableAllRules", function(){
|
128
|
+
$(".rule input[type='checkbox']:not(:checked)").click();
|
129
|
+
});
|
130
|
+
},
|
131
|
+
disableAllRules: function(){
|
132
|
+
$(document).on("click", "#disableAllRules", function(){
|
133
|
+
$(".rule input[type='checkbox']:checked").click();
|
134
|
+
});
|
135
|
+
},
|
136
|
+
changeRuleStatus: function(){
|
137
|
+
$(document).on("click", ".rule input[type='checkbox']", function(){
|
138
|
+
var enabled = $(this).is(":checked"),
|
139
|
+
$rule = $(this).closest(".rule"),
|
140
|
+
phrase = $rule.find("[data-phrase]").html(),
|
141
|
+
symbol = $rule.data("rule-symbol"),
|
142
|
+
improvementPhraseList = [];
|
143
|
+
if(enabled){
|
144
|
+
localStorage.removeItem(symbol);
|
145
|
+
} else {
|
146
|
+
localStorage.setItem(symbol, true);
|
147
|
+
}
|
148
|
+
if(phrase.indexOf("{class}") != -1) {
|
149
|
+
var targets = $(this).closest(".rule").find("[data-targets]").html().trim().split(", ");
|
150
|
+
if(targets.indexOf("Scenario") != -1){
|
151
|
+
targets.push("Scenario Outline");
|
152
|
+
}
|
153
|
+
$.each(targets, function (n, target) {
|
154
|
+
improvementPhraseList.push(phrase.replace("{class}", target))
|
155
|
+
});
|
156
|
+
} else if(phrase.indexOf("{word}") != -1){
|
157
|
+
var targets = $(this).closest(".rule").find("[data-conditions]").html().trim().replace("[", "").replace("]", "").replace(/\"/g, "").split(", ");
|
158
|
+
$.each(targets, function (n, target) {
|
159
|
+
improvementPhraseList.push(phrase.replace("{word}", target))
|
160
|
+
});
|
161
|
+
} else {
|
162
|
+
improvementPhraseList.push(phrase)
|
163
|
+
}
|
164
|
+
(enabled) ? CukeSniffer.actions._enableRule(improvementPhraseList) : CukeSniffer.actions._disableRule(improvementPhraseList);
|
165
|
+
});
|
166
|
+
},
|
167
|
+
_enableRule: function(improvementPhraseList){
|
168
|
+
$.each(improvementPhraseList, function(n, phrase){
|
169
|
+
var $all = $("[data-improvement-row]:has([data-improvement='" + phrase +"'])");
|
170
|
+
$all.show();
|
171
|
+
$all.attr("show", true);
|
172
|
+
});
|
173
|
+
CukeSniffer.view._updateVisibleItems();
|
174
|
+
},
|
175
|
+
_disableRule: function(improvementPhraseList){
|
176
|
+
$.each(improvementPhraseList, function(n, phrase){
|
177
|
+
var $all = $("[data-improvement-row]:has([data-improvement='" + phrase +"'])");
|
178
|
+
$all.hide();
|
179
|
+
$all.removeAttr("show");
|
180
|
+
});
|
181
|
+
CukeSniffer.view._updateVisibleItems();
|
182
|
+
}
|
183
|
+
}
|
184
|
+
};
|
@@ -1,133 +1,123 @@
|
|
1
|
-
<link rel="stylesheet"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
background-color: #509f50;
|
15
|
-
color:
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}
|
25
|
-
|
26
|
-
.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
}
|
51
|
-
|
52
|
-
.
|
53
|
-
padding
|
54
|
-
}
|
55
|
-
|
56
|
-
.
|
57
|
-
|
58
|
-
|
59
|
-
}
|
60
|
-
|
61
|
-
.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
display: inline;
|
85
|
-
}
|
86
|
-
|
87
|
-
.
|
88
|
-
|
89
|
-
}
|
90
|
-
|
91
|
-
.
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
margin: 0.25%;
|
124
|
-
}
|
125
|
-
|
126
|
-
ol {
|
127
|
-
list-style-type: none;
|
128
|
-
display: block;
|
129
|
-
padding-left: 1%
|
130
|
-
|
131
|
-
}
|
132
|
-
|
1
|
+
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
2
|
+
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-XdYbMnZ/QjLh6iI4ogqCTaIjrFk87ip+ekIjefZch0Y+PvJ8CDYtEs1ipDmPorQ+" crossorigin="anonymous">
|
3
|
+
|
4
|
+
<style>
|
5
|
+
|
6
|
+
h1 small {
|
7
|
+
font-size: 16px;
|
8
|
+
}
|
9
|
+
.panel-default {
|
10
|
+
border-color: #509f50;
|
11
|
+
}
|
12
|
+
|
13
|
+
.panel-default>.panel-heading {
|
14
|
+
background-color: #509f50;
|
15
|
+
border-color: #3a753a;
|
16
|
+
color: white;
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
.table-striped>tbody>tr:nth-of-type(odd),
|
21
|
+
.improvement:nth-of-type(odd),
|
22
|
+
.blueRow {
|
23
|
+
background-color: #ccffff;
|
24
|
+
}
|
25
|
+
|
26
|
+
.glyphicon-question-sign:before {
|
27
|
+
color: #509f50;
|
28
|
+
}
|
29
|
+
|
30
|
+
.small {
|
31
|
+
display: none;
|
32
|
+
}
|
33
|
+
|
34
|
+
.panel-body {
|
35
|
+
padding:0px;
|
36
|
+
}
|
37
|
+
|
38
|
+
.panel-body>div>div {
|
39
|
+
padding: 8px;
|
40
|
+
font-size: 16px;
|
41
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
42
|
+
}
|
43
|
+
|
44
|
+
.red {
|
45
|
+
color: rgba(208, 33, 33, 0.73);
|
46
|
+
}
|
47
|
+
|
48
|
+
.black {
|
49
|
+
color: black;
|
50
|
+
}
|
51
|
+
|
52
|
+
.button-bar {
|
53
|
+
padding: 10px;
|
54
|
+
}
|
55
|
+
|
56
|
+
.button-bar .btn {
|
57
|
+
margin-top: 10px;
|
58
|
+
margin-bottom: 10px
|
59
|
+
}
|
60
|
+
|
61
|
+
.button-bar .btn-group {
|
62
|
+
margin-left: 10px;
|
63
|
+
}
|
64
|
+
|
65
|
+
.rule, .deadStep, .feature,
|
66
|
+
.stepDefinition, .hook {
|
67
|
+
cursor: pointer;
|
68
|
+
}
|
69
|
+
|
70
|
+
.details .well{
|
71
|
+
margin-bottom: 0px;
|
72
|
+
cursor: default;
|
73
|
+
}
|
74
|
+
|
75
|
+
.scenario > .title {
|
76
|
+
margin-top: 15px;
|
77
|
+
}
|
78
|
+
|
79
|
+
.well .well {
|
80
|
+
background-color: white;
|
81
|
+
}
|
82
|
+
|
83
|
+
.table.collapse.in {
|
84
|
+
display: inline-table;
|
85
|
+
}
|
86
|
+
|
87
|
+
.panel-heading {
|
88
|
+
cursor: pointer;
|
89
|
+
}
|
90
|
+
|
91
|
+
.filename {
|
92
|
+
text-overflow: ellipsis;
|
93
|
+
white-space: nowrap;
|
94
|
+
overflow: hidden;
|
95
|
+
}
|
96
|
+
|
97
|
+
.small-page {
|
98
|
+
display: none;
|
99
|
+
}
|
100
|
+
|
101
|
+
@media screen and (max-width: 993px){
|
102
|
+
#ruleFilters {
|
103
|
+
padding-left: 10px;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
@media screen and (max-width: 480px){
|
108
|
+
.full-page {
|
109
|
+
display: none;
|
110
|
+
}
|
111
|
+
.small-page {
|
112
|
+
display: inline;
|
113
|
+
}
|
114
|
+
|
115
|
+
#rules_data .enable-bar {
|
116
|
+
padding-left: 10px;
|
117
|
+
}
|
118
|
+
|
119
|
+
.ruleFilters .small-page {
|
120
|
+
font-size: 20px;
|
121
|
+
}
|
122
|
+
}
|
133
123
|
</style>
|
@@ -1,63 +1,63 @@
|
|
1
|
-
<div class="
|
2
|
-
|
3
|
-
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
<% end %>
|
21
|
-
<% unless dead_steps.empty? or !cuke_sniffer.cataloged? %>
|
22
|
-
<table id="dead_steps_table" style="width:100%; border-top: 2px solid #509f50" border="0" cellspacing="0">
|
23
|
-
<% index = 0 %>
|
24
|
-
<% dead_steps.each_key do |file_name| %>
|
25
|
-
<% if index.odd? %>
|
26
|
-
<tr class="notes blue_title_row" onclick="updateDisplayStatus(document.getElementById('dead_step_file_detail_<%=index%>')); updateDivScroll('dead_steps_data', 'dead_steps_table');">
|
1
|
+
<div class="row">
|
2
|
+
<div class="panel panel-default">
|
3
|
+
<div class="panel-heading panel-title" data-toggle="collapse" data-target="#deadSteps" >
|
4
|
+
Dead Steps <span class="glyphicon glyphicon-menu-down"></span>
|
5
|
+
</div>
|
6
|
+
<div id="deadSteps" class="panel-body collapse">
|
7
|
+
<span class="button-bar">
|
8
|
+
<%= build_page(cuke_sniffer, "expand_and_collapse_buttons.html.erb") %>
|
9
|
+
</span>
|
10
|
+
<div>
|
11
|
+
<% dead_steps = cuke_sniffer.get_dead_steps %>
|
12
|
+
<% dead_steps.delete(:total) %>
|
13
|
+
<% dead_step_count = dead_steps.values.flatten.count %>
|
14
|
+
<% if !cuke_sniffer.cataloged? %>
|
15
|
+
<div class="notes">Steps were not cataloged.</div>
|
16
|
+
<% elsif dead_step_count == 0 %>
|
17
|
+
<div class="notes">No dead steps found!</div>
|
18
|
+
<% elsif dead_step_count == 1 %>
|
19
|
+
<div class="notes"><%= dead_step_count %> Dead Step found.</div>
|
27
20
|
<% else %>
|
28
|
-
<
|
21
|
+
<div class="notes"><%= dead_step_count %> Dead Steps found.</div>
|
29
22
|
<% end %>
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
</div>
|
63
|
-
|
23
|
+
</div>
|
24
|
+
<% unless dead_steps.empty? or !cuke_sniffer.cataloged? %>
|
25
|
+
<% dead_steps.each_key do |file_name| %>
|
26
|
+
<div class="deadStep">
|
27
|
+
<div class="row">
|
28
|
+
<div class="title col-md-12">
|
29
|
+
<div class="col-md-1">
|
30
|
+
<span class="red" title="Total dead steps in file.">
|
31
|
+
<%= dead_steps[file_name].size %>
|
32
|
+
</span>
|
33
|
+
</div>
|
34
|
+
<div class="col-md-11 filename">
|
35
|
+
<%= file_name.gsub(cuke_sniffer.step_definitions_location, "").gsub(cuke_sniffer.step_definitions_location, "") %>.rb
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
<div class="details col-md-12" style="display:none;">
|
39
|
+
<div class="well">
|
40
|
+
<div class="row">
|
41
|
+
<div class="col-md-12 filename">
|
42
|
+
File: <a target="_blank" href="file:///<%= file_name %>.rb" title="Note: Links to file on system this report was generated.">
|
43
|
+
<%= file_name.gsub(cuke_sniffer.step_definitions_location, "").gsub(cuke_sniffer.step_definitions_location, "") %>.rb
|
44
|
+
</a>
|
45
|
+
</div>
|
46
|
+
<% dead_steps[file_name].each do |regex| %>
|
47
|
+
<div class="col-md-11">
|
48
|
+
<%= regex.match(/:(?<regex>.*)/)[:regex] %>
|
49
|
+
</div>
|
50
|
+
<div class="col-md-1">
|
51
|
+
Line: <%= regex.match(/(?<line>\d+):/)[:line] %>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
<% end %>
|
60
|
+
<% end %>
|
61
|
+
</div>
|
62
|
+
</div>
|
63
|
+
</div>
|