caboose-cms 0.0.7 → 0.1.0

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 (57) hide show
  1. data/app/assets/images/caboose/caboose_nav.png +0 -0
  2. data/app/assets/javascripts/caboose/admin.js +37 -0
  3. data/app/assets/javascripts/caboose/application.js +2 -1
  4. data/app/assets/javascripts/caboose/model.form.page.js +30 -0
  5. data/app/assets/javascripts/caboose/station.js +223 -0
  6. data/app/assets/stylesheets/caboose/admin.css +57 -0
  7. data/app/assets/stylesheets/caboose/caboose.css +146 -16
  8. data/app/assets/stylesheets/caboose/tinymce.css +25 -0
  9. data/app/controllers/caboose/pages_controller.rb +43 -19
  10. data/app/controllers/caboose/permissions_controller.rb +91 -73
  11. data/app/controllers/caboose/roles_controller.rb +49 -37
  12. data/app/controllers/caboose/settings_controller.rb +100 -0
  13. data/app/controllers/caboose/station_controller.rb +13 -0
  14. data/app/controllers/caboose/users_controller.rb +51 -44
  15. data/app/models/caboose/caboose_plugin.rb +2 -6
  16. data/app/models/caboose/page.rb +3 -3
  17. data/app/models/caboose/page_bar_generator.rb +37 -28
  18. data/app/models/caboose/page_plugin.rb +43 -0
  19. data/app/models/caboose/permission_plugin.rb +27 -0
  20. data/app/models/caboose/role.rb +19 -1
  21. data/app/models/caboose/role_plugin.rb +27 -0
  22. data/app/models/caboose/setting.rb +5 -0
  23. data/app/models/caboose/settings_plugin.rb +27 -0
  24. data/app/models/caboose/user.rb +1 -2
  25. data/app/models/caboose/user_plugin.rb +28 -0
  26. data/app/views/caboose/pages/edit.html.erb +7 -27
  27. data/app/views/caboose/pages/edit_settings.html.erb +38 -0
  28. data/app/views/caboose/pages/sitemap.html.erb +12 -0
  29. data/app/views/caboose/permissions/edit.html.erb +21 -4
  30. data/app/views/caboose/permissions/index.html.erb +29 -21
  31. data/app/views/caboose/permissions/new.html.erb +22 -2
  32. data/app/views/caboose/roles/edit.html.erb +40 -27
  33. data/app/views/caboose/roles/index.html.erb +3 -5
  34. data/app/views/caboose/roles/new.html.erb +30 -11
  35. data/app/views/caboose/settings/edit.html.erb +23 -0
  36. data/app/views/caboose/settings/index.html.erb +33 -0
  37. data/app/views/caboose/settings/new.html.erb +25 -0
  38. data/app/views/caboose/users/edit.html.erb +11 -14
  39. data/app/views/caboose/users/index.html.erb +2 -4
  40. data/app/views/caboose/users/new.html.erb +2 -0
  41. data/app/views/caboose/users/update_pic.html.erb +1 -1
  42. data/app/views/layouts/caboose/_station.html.erb +43 -14
  43. data/app/views/layouts/caboose/{caboose.html.erb → admin.html.erb} +2 -2
  44. data/config/routes.rb +16 -5
  45. data/lib/caboose.rb +1 -1
  46. data/lib/caboose/engine.rb +15 -6
  47. data/lib/caboose/version.rb +1 -1
  48. data/lib/sample_files/tinymce.yml +22 -0
  49. data/lib/tasks/caboose.rake +21 -1
  50. metadata +40 -12
  51. data/app/assets/javascripts/caboose/caboose.js +0 -28
  52. data/app/assets/javascripts/caboose/permissions.js +0 -0
  53. data/app/assets/javascripts/caboose/roles.js +0 -35
  54. data/app/assets/javascripts/caboose/users.js +0 -38
  55. data/app/views/caboose/permissions/show.html.erb +0 -15
  56. data/app/views/caboose/roles/show.html.erb +0 -20
  57. data/app/views/caboose/users/update_resume.html.erb +0 -14
@@ -0,0 +1,5 @@
1
+
2
+ class Caboose::Setting < ActiveRecord::Base
3
+ self.table_name = "settings"
4
+ attr_accessible :name, :value
5
+ end
@@ -0,0 +1,27 @@
1
+ class Caboose::SettingsPlugin < Caboose::CaboosePlugin
2
+
3
+ def self.admin_nav(nav, user, page)
4
+ return nav if user.nil? || !user.is_allowed('settings', 'view')
5
+
6
+ item = {
7
+ 'id' => 'settings',
8
+ 'text' => 'Settings',
9
+ 'children' => []
10
+ }
11
+ if (user.is_allowed('settings', 'view'))
12
+ item['children'] << {
13
+ 'href' => '/admin/settings',
14
+ 'text' => 'View All Settings'
15
+ }
16
+ end
17
+ if (user.is_allowed('settings', 'add'))
18
+ item['children'] << {
19
+ 'href' => '/admin/settings/new',
20
+ 'text' => 'New Setting'
21
+ }
22
+ end
23
+ nav << item
24
+ return nav
25
+ end
26
+
27
+ end
@@ -1,8 +1,7 @@
1
1
 
2
2
  class Caboose::User < ActiveRecord::Base
3
3
  self.table_name = "users"
4
- has_and_belongs_to_many :roles
5
- has_many :workouts
4
+ has_and_belongs_to_many :roles
6
5
  attr_accessible :email, :first_name, :last_name, :username, :token, :password
7
6
 
8
7
  LOGGED_OUT_USER_ID = 2
@@ -0,0 +1,28 @@
1
+ class Caboose::UserPlugin < Caboose::CaboosePlugin
2
+
3
+ def self.admin_nav(nav, user, page)
4
+ return nav if user.nil? || !user.is_allowed('users', 'view')
5
+
6
+ item = {
7
+ 'id' => 'users',
8
+ 'href' => '/admin/users',
9
+ 'text' => 'Users',
10
+ 'children' => []
11
+ }
12
+ if (user.is_allowed('users', 'view'))
13
+ item['children'] << {
14
+ 'href' => '/admin/users',
15
+ 'text' => 'View All Users'
16
+ }
17
+ end
18
+ if (user.is_allowed('users', 'add'))
19
+ item['children'] << {
20
+ 'href' => '/admin/users/new',
21
+ 'text' => 'New User'
22
+ }
23
+ end
24
+ nav << item
25
+ return nav
26
+ end
27
+
28
+ end
@@ -1,45 +1,25 @@
1
- <%
2
-
3
- def json(obj, defaultvalue = "")
4
- return defaultvalue.to_json if obj.nil?
5
- return obj.to_json
6
- end
7
1
 
8
- %>
9
- <h1>Edit Page</h1>
2
+ <h1>Edit Page Content</h1>
3
+ <p class='current_page_editing'>Currently editing: /<%= @page.uri %></p>
10
4
 
11
5
  <div id='page_<%= @page.id %>_container'></div>
12
6
 
13
7
  <% content_for :caboose_js do %>
8
+ <%= javascript_include_tag "caboose/model.form.page" %>
14
9
  <script type="text/javascript">
15
10
 
16
11
  $(document).ready(function() {
17
12
  var page = new Model({
18
13
  name: 'Page',
19
14
  id: <%= @page.id %>,
15
+ form: 'Model.Form.Page',
20
16
  attributes: [
21
- { name: 'title' , type: 'text' , value: <%= raw json(@page.title ) %> },
22
- { name: 'content' , type: 'textarea' , value: <%= raw json(@page.content ) %> },
23
- { name: 'parent_id' , type: 'select' , value: <%= raw json(@page.parent_id, -1 ) %>, text: <%= raw (@page.parent_id == -1 ? "[No Parent]" : @page.parent.title).to_json %>, options_url: '/pages/sitemap-options' },
24
- { name: 'menu_title' , type: 'text' , value: <%= raw json(@page.menu_title ) %> },
25
- { name: 'slug' , type: 'text' , value: <%= raw json(@page.slug ) %> },
26
- { name: 'alias' , type: 'text' , value: <%= raw json(@page.alias ) %> },
27
- { name: 'redirect_url' , type: 'text' , value: <%= raw json(@page.redirect_url ) %> },
28
- { name: 'hide' , type: 'checkbox' , value: <%= raw @page.hide %> },
29
- { name: 'content_format' , type: 'select' , value: <%= raw json(@page.content_format ) %> },
30
- { name: 'custom_css' , type: 'textarea' , value: <%= raw json(@page.custom_css ) %> },
31
- { name: 'custom_js' , type: 'textarea' , value: <%= raw json(@page.custom_js ) %> },
32
- { name: 'layout' , type: 'text' , value: <%= raw json(@page.layout ) %> },
33
- { name: 'custom_sort_children' , type: 'select' , value: <%= raw json(@page.custom_sort_children ) %>, text: "" },
34
- { name: 'seo_title' , type: 'text' , value: <%= raw json(@page.seo_title ) %> },
35
- { name: 'meta_description' , type: 'text' , value: <%= raw json(@page.meta_description ) %> },
36
- { name: 'meta_robots' , type: 'checkbox-multiple' , value: <%= raw (@page.meta_robots.nil? ? [] : @page.meta_robots.split(', ')).to_json %>, text: <%= raw (@page.meta_robots.nil? ? "[Empty]" : @page.meta_robots).to_json %>, options_url: '/pages/robots-options' },
37
- { name: 'canonical_url' , type: 'text' , value: <%= raw json(@page.canonical_url ) %> },
38
- { name: 'fb_description' , type: 'text' , value: <%= raw json(@page.fb_description ) %> },
39
- { name: 'gp_description' , type: 'text' , value: <%= raw json(@page.gp_description ) %> }
17
+ { name: 'title' , type: 'h1' , value: <%= raw Caboose.json(@page.title ) %>, width: '500px' },
18
+ { name: 'content' , type: 'rich-text' , value: <%= raw Caboose.json(@page.content ) %> }
40
19
  ]
41
20
  });
42
21
  });
43
22
 
44
23
  </script>
24
+ <%= tinymce :caboose %>
45
25
  <% end %>
@@ -0,0 +1,38 @@
1
+
2
+ <h1>Edit Page Settings</h1>
3
+ <p class='current_page_editing'>Currently editing: /<%= @page.uri %></p>
4
+
5
+ <div id='page_<%= @page.id %>_container'></div>
6
+
7
+ <% content_for :caboose_js do %>
8
+ <script type="text/javascript">
9
+
10
+ $(document).ready(function() {
11
+ var page = new Model({
12
+ name: 'Page',
13
+ id: <%= @page.id %>,
14
+ attributes: [
15
+ { name: 'parent_id' , nice_name: 'Parent' , type: 'select' , value: <%= raw Caboose.json(@page.parent_id, -1 ) %>, text: <%= raw (@page.parent_id == -1 ? "[No Parent]" : @page.parent.title).to_json %>, options_url: '/pages/sitemap-options' },
16
+ { name: 'menu_title' , nice_name: 'Menu title' , type: 'text' , value: <%= raw Caboose.json(@page.menu_title ) %> },
17
+ { name: 'slug' , nice_name: 'Slug' , type: 'text' , value: <%= raw Caboose.json(@page.slug ) %> },
18
+ { name: 'alias' , nice_name: 'Alias' , type: 'text' , value: <%= raw Caboose.json(@page.alias ) %> },
19
+ { name: 'redirect_url' , nice_name: 'Redirect URL' , type: 'text' , value: <%= raw Caboose.json(@page.redirect_url ) %> },
20
+ { name: 'hide' , nice_name: 'Hide' , type: 'checkbox' , value: <%= raw @page.hide %> },
21
+ { name: 'content_format' , nice_name: 'Content format' , type: 'select' , value: <%= raw Caboose.json(@page.content_format ) %> },
22
+ { name: 'custom_css' , nice_name: 'Custom CSS' , type: 'textarea' , value: <%= raw Caboose.json(@page.custom_css ) %> },
23
+ { name: 'custom_js' , nice_name: 'Custom javascript' , type: 'textarea' , value: <%= raw Caboose.json(@page.custom_js ) %> },
24
+ { name: 'layout' , nice_name: 'Layout' , type: 'text' , value: <%= raw Caboose.json(@page.layout ) %> },
25
+ { name: 'custom_sort_children' , nice_name: 'Custom sort child pages' , type: 'checkbox' , value: <%= raw @page.custom_sort_children%> },
26
+ { name: 'seo_title' , nice_name: 'SEO title' , type: 'text' , value: <%= raw Caboose.json(@page.seo_title ) %> },
27
+ { name: 'meta_description' , nice_name: 'Meta description' , type: 'text' , value: <%= raw Caboose.json(@page.meta_description ) %> },
28
+ { name: 'meta_robots' , nice_name: 'Meta robots' , type: 'checkbox-multiple' , value: <%= raw (@page.meta_robots.nil? ? [] : @page.meta_robots.split(', ')).to_json %>, text: <%= raw (@page.meta_robots.nil? ? "[Empty]" : @page.meta_robots).to_json %>, options_url: '/pages/robots-options' },
29
+ { name: 'canonical_url' , nice_name: 'Canonical URL' , type: 'text' , value: <%= raw Caboose.json(@page.canonical_url ) %> },
30
+ { name: 'fb_description' , nice_name: 'Facebook description' , type: 'text' , value: <%= raw Caboose.json(@page.fb_description ) %> },
31
+ { name: 'gp_description' , nice_name: 'Google+ description' , type: 'text' , value: <%= raw Caboose.json(@page.gp_description ) %> }
32
+ ]
33
+ });
34
+ });
35
+
36
+ </script>
37
+ <%= tinymce :caboose %>
38
+ <% end %>
@@ -0,0 +1,12 @@
1
+
2
+ <style type='text/css'>
3
+ #content li.hidden a { color: #990000; }
4
+ </style>
5
+
6
+ <h1>Page Sitemap</h1>
7
+
8
+ <ul>
9
+ <% @options.each do |opt| %>
10
+ <li><a href='<%= opt.title
11
+ this->addPageItems($p);
12
+ </ul>\n";
@@ -1,6 +1,23 @@
1
- <h1>Editing permission</h1>
1
+
2
+ <h1>Edit Permission</h1>
3
+ <div id='permission_<%= @permission.id %>_container'></div>
2
4
 
3
- <%= render 'form' %>
5
+ <% content_for :caboose_js do %>
6
+ <script type="text/javascript">
4
7
 
5
- <%= link_to 'Show', @permission %> |
6
- <%= link_to 'Back', permissions_path %>
8
+ $(document).ready(function() {
9
+ var permission = new Model({
10
+ name: 'Permission',
11
+ id: <%= @permission.id %>,
12
+ listing_url: 'get /admin/permissions',
13
+ update_url: 'put /admin/permissions/<%= @permission.id %>',
14
+ delete_url: 'delete /admin/permissions/<%= @permission.id %>',
15
+ attributes: [
16
+ { name: 'resource' , type: 'text', value: "<%= @permission.resource %>" },
17
+ { name: 'action2' , type: 'text', value: "<%= @permission.action %>", nice_name: 'Action' }
18
+ ]
19
+ });
20
+ });
21
+
22
+ </script>
23
+ <% end %>
@@ -1,25 +1,33 @@
1
- <h1>Listing permissions</h1>
1
+ <h1>Permissions</h1>
2
2
 
3
- <table>
4
- <tr>
5
- <th>Resource</th>
6
- <th>Action</th>
7
- <th></th>
8
- <th></th>
9
- <th></th>
10
- </tr>
11
-
12
- <% @permissions.each do |permission| %>
13
- <tr>
14
- <td><%= permission.resource %></td>
15
- <td><%= permission.action %></td>
16
- <td><%= link_to 'Show', permission %></td>
17
- <td><%= link_to 'Edit', edit_permission_path(permission) %></td>
18
- <td><%= link_to 'Destroy', permission, method: :delete, data: { confirm: 'Are you sure?' } %></td>
19
- </tr>
20
- <% end %>
3
+ <form action='/admin/permissions' method='get'>
4
+ <table cellpadding='4' cellspacing='0' border='0'>
5
+ <tr><td>Resource: </td><td><input type='text' name='resource' value='<%= @gen.params['resource'] %>' style='width: 200px;' /></td></tr>
21
6
  </table>
7
+ <p><input type='submit' value='Search' /></p>
8
+ </form>
9
+
10
+ <% if (@permissions.count > 0) %>
11
+
12
+ <table class='data'>
13
+ <tr>
14
+ <%= raw @gen.sortable_table_headings({
15
+ 'resource' => 'Resource',
16
+ 'action' => 'Action'
17
+ })
18
+ %>
19
+ </tr>
20
+ <% @permissions.each do |perm| %>
21
+ <tr onclick="window.location='/admin/permissions/<%= perm.id %>/edit';">
22
+ <td><%= perm.resource %></td>
23
+ <td><%= perm.action %></td>
24
+ </tr>
25
+ <% end %>
26
+ </table>
27
+ <p><%= raw @gen.generate %></p>
28
+
29
+ <% else %>
22
30
 
23
- <br />
31
+ <p>There are no permissions.</p>
24
32
 
25
- <%= link_to 'New Permission', new_permission_path %>
33
+ <% end %>
@@ -1,5 +1,25 @@
1
+
1
2
  <h1>New permission</h1>
3
+ <div id='permission_new_container'></div>
4
+
5
+ <% content_for :caboose_js do %>
6
+ <script type='text/javascript'>
7
+
8
+ var permission = false;
9
+ $(document).ready(function() {
2
10
 
3
- <%= render 'form' %>
11
+ permission = new Model({
12
+ name: 'Permission',
13
+ id: 'new',
14
+ listing_url: 'get /admin/permissions',
15
+ create_url: 'post /admin/permissions',
16
+ attributes: [
17
+ { name: 'resource' , type: 'text', value: '' },
18
+ { name: 'action2' , type: 'text', value: '', nice_name: 'Action' }
19
+ ]
20
+ });
21
+
22
+ });
4
23
 
5
- <%= link_to 'Back', permissions_path %>
24
+ </script>
25
+ <% end %>
@@ -1,36 +1,49 @@
1
1
 
2
2
  <h1>Edit Role</h1>
3
-
4
- <table>
5
- <tr><td>Parent: </td><td id='parent_id_container'></td></tr>
6
- <tr><td>Name: </td><td id='name_container'></td></tr>
7
- <tr><td>Description: </td><td id='description_container'></td></tr>
8
- <tr><td>Members: </td><td id='members_container'></td></tr>
9
- </table>
10
- <div id='message'></div>
11
- <p>
12
- <input type='button' value='Back' onclick="window.location='/roles';" />
13
- <input type='button' value='Delete Role' onclick="Caboose.Role.delete(<%= @role.id %>);" />
14
- </p>
3
+ <div id='role_<%= @role.id %>_container'></div>
15
4
 
16
5
  <% content_for :caboose_js do %>
17
- <%= javascript_include_tag "caboose/roles" %>
18
6
  <script type="text/javascript">
7
+
19
8
  $(document).ready(function() {
20
- var fields = [
21
- <%
22
- membersValue = @users.collect{|u| u.id}.to_json
23
- membersText = @users.collect{|u| r.first_name + " " u.last_name}.join('<br />')
24
- %>
25
- { id: 'name' , field_type: 'text' , value: "<%= @role.name %>" },
26
- { id: 'description' , field_type: 'text' , value: "<%= @role.description %>" },
27
- { id: 'parent_id' , field_type: 'select', value: <%= @role.parent_id %> , text: "<%= @role.parent.name %>", empty_text: '[Empty]' , loading_message: 'Getting roles...', options_url: '/roles/options' },
28
- { id: 'members' , field_type: 'select', value: <%= membersValue %> , text: "<%= membersText %>" , empty_text: '[No roles]', loading_message: 'Getting users...', options_url: '/users/options', multiple: true }
29
- ];
30
- var params = {
31
- update_url: '/roles/<%= @role.id %>'
32
- };
33
- $.quickEdit.init(fields, params);
9
+ var user = new Model({
10
+ name: 'Role',
11
+ id: <%= @role.id %>,
12
+ listing_url: 'get /admin/roles',
13
+ update_url: 'put /admin/roles/<%= @role.id %>',
14
+ delete_url: 'delete /admin/roles/<%= @role.id %>',
15
+ attributes: [
16
+ {
17
+ name: 'parent_id',
18
+ nice_name: 'Parent',
19
+ type: 'select',
20
+ value: <%= @role.parent_id %>,
21
+ text: "<%= @role.parent.nil? ? '[No parent]' : @role.parent.name %>",
22
+ show_controls: true,
23
+ loading_message: 'Getting roles...',
24
+ options_url: '/admin/roles/options'
25
+ },
26
+ { name: 'name' , type: 'text' , value: "<%= @role.name %>" },
27
+ { name: 'description' , type: 'text' , value: "<%= @role.description %>" },
28
+ {
29
+ name: 'members',
30
+ type: 'checkbox-multiple',
31
+ value: <%= @role.users.collect{|u| u.id}.to_json %>,
32
+ text: "<%= raw @role.users.order('last_name, first_name').collect{|u| "#{u.first_name} #{u.last_name}"}.join('<br />') %>",
33
+ loading_message: 'Getting users...',
34
+ options_url: '/admin/users/options',
35
+ },
36
+ {
37
+ name: 'permissions',
38
+ type: 'checkbox-multiple',
39
+ value: <%= @role.permissions.collect{|p| p.id}.to_json %>,
40
+ text: "<%= raw @role.permissions.order('resource, action').collect{|p| "#{p.resource}_#{p.action}"}.join('<br />') %>",
41
+ loading_message: 'Getting permissions...',
42
+ options_url: '/admin/permissions/options',
43
+ }
44
+ ]
45
+ });
34
46
  });
47
+
35
48
  </script>
36
49
  <% end %>
@@ -1,14 +1,12 @@
1
1
  <h1>Roles</h1>
2
2
 
3
- <p><a href='/roles/new'>New Role</a></p>
4
-
5
3
  <table class='data'>
6
4
  <tr>
7
5
  <th>Name</th>
8
6
  </tr>
9
- <% @roles.each do |arr| %>
10
- <tr onclick="window.location=/roles/<%= arr['value'] %>/edit';">
11
- <td><%= arr['text'] %></td>
7
+ <% @roles.each do |r| %>
8
+ <tr onclick="window.location='/admin/roles/<%= r['value'] %>/edit';">
9
+ <td><%= r['text'] %></td>
12
10
  </tr>
13
11
  <% end %>
14
12
  </table>
@@ -1,16 +1,35 @@
1
1
 
2
2
  <h1>New role</h1>
3
-
4
- <form action='/roles' method='post' id='new_role_form' onsubmit='return false;'>
5
- <p>Parent: <input type='text' name='parent_id' style='width: 200px;' /></p>
6
- <p>Name: <input type='text' name='name' style='width: 200px;' /></p>
7
- <div id='message'></div>
8
- <p>
9
- <input type='submit' value='Add Role' onclick='Caboose.Role.add();' />
10
- <input type='button' value='Back' onclick="window.location='/roles';" />
11
- </p>
12
- </form>
3
+ <div id='role_new_container'></div>
13
4
 
14
5
  <% content_for :caboose_js do %>
15
- <%= javascript_include_tag "caboose/roles" %>
6
+ <script type='text/javascript'>
7
+
8
+ var role = false;
9
+ $(document).ready(function() {
10
+
11
+ role = new Model({
12
+ name: 'Role',
13
+ id: 'new',
14
+ listing_url: 'get /admin/roles',
15
+ create_url: 'post /admin/roles',
16
+ attributes: [
17
+ {
18
+ name: 'parent_id',
19
+ type: 'select',
20
+ value: -1,
21
+ text: "[No Parent]",
22
+ options_url: '/admin/roles/options'
23
+ },
24
+ {
25
+ name: 'name',
26
+ type: 'text',
27
+ value: ''
28
+ }
29
+ ]
30
+ });
31
+
32
+ });
33
+
34
+ </script>
16
35
  <% end %>
@@ -0,0 +1,23 @@
1
+
2
+ <h1>Edit Setting</h1>
3
+ <div id='setting_<%= @setting.id %>_container'></div>
4
+
5
+ <% content_for :caboose_js do %>
6
+ <script type="text/javascript">
7
+
8
+ $(document).ready(function() {
9
+ var setting = new Model({
10
+ name: 'Setting',
11
+ id: <%= @setting.id %>,
12
+ listing_url: 'get /admin/settings',
13
+ update_url: 'put /admin/settings/<%= @setting.id %>',
14
+ delete_url: 'delete /admin/settings/<%= @setting.id %>',
15
+ attributes: [
16
+ { name: 'name' , type: 'text' , value: "<%= @setting.name %>" },
17
+ { name: 'value' , type: 'textarea' , value: "<%= @setting.value %>" }
18
+ ]
19
+ });
20
+ });
21
+
22
+ </script>
23
+ <% end %>