aerogel-admin 1.4.4

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 (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +13 -0
  6. data/Rakefile +1 -0
  7. data/aerogel-admin.gemspec +31 -0
  8. data/app/helpers/admin.rb +54 -0
  9. data/app/helpers/decorators.rb +29 -0
  10. data/app/helpers/icons.rb +11 -0
  11. data/app/helpers/table_builder.rb +8 -0
  12. data/app/helpers/tabs_builder.rb +7 -0
  13. data/app/routes/admin.rb +37 -0
  14. data/app/routes/users.rb +71 -0
  15. data/app/routes/users_access.rb +54 -0
  16. data/app/routes/users_roles.rb +53 -0
  17. data/assets/fonts/aerogel-icons.css +28 -0
  18. data/assets/fonts/aerogel-icons.eot +0 -0
  19. data/assets/fonts/aerogel-icons.svg +11 -0
  20. data/assets/fonts/aerogel-icons.ttf +0 -0
  21. data/assets/fonts/aerogel-icons.woff +0 -0
  22. data/assets/javascripts/.gitkeep +0 -0
  23. data/assets/javascripts/controllers/admin-modal.js.coffee +7 -0
  24. data/assets/javascripts/controllers/admin-modal/admin-modal-form-buttons.js.coffee +56 -0
  25. data/assets/javascripts/controllers/admin.js.coffee +13 -0
  26. data/assets/javascripts/controllers/admin/selectize-inputs.js.coffee +7 -0
  27. data/assets/javascripts/controllers/admin/top-menu-shadow.js.coffee +9 -0
  28. data/assets/javascripts/utils/ajax-spinner.js.coffee +26 -0
  29. data/assets/javascripts/utils/ajax-watcher.js.coffee +18 -0
  30. data/assets/javascripts/utils/bootstrap-modal-reload.js.coffee +8 -0
  31. data/assets/javascripts/utils/form-data-async.js.coffee +27 -0
  32. data/assets/javascripts/utils/i18n.js.coffee +27 -0
  33. data/assets/javascripts/utils/on-future-elements.js.coffee +15 -0
  34. data/assets/stylesheets/admin/bootstrap-settings.css.scss +33 -0
  35. data/assets/stylesheets/admin/global.css.scss +61 -0
  36. data/assets/stylesheets/admin/styles/ajax-indicator.css.scss +26 -0
  37. data/assets/stylesheets/admin/styles/bootstrap-modal.css.scss +24 -0
  38. data/assets/stylesheets/admin/styles/language-selector.css.scss +5 -0
  39. data/assets/stylesheets/admin/styles/page-header.css.scss +7 -0
  40. data/assets/stylesheets/admin/styles/sticky-footer-navbar.css.scss +34 -0
  41. data/assets/stylesheets/admin/styles/table.css.scss +3 -0
  42. data/assets/stylesheets/admin/styles/top-menu.css.scss +3 -0
  43. data/assets/stylesheets/admin/utils/center-absolutely.css.scss +5 -0
  44. data/assets/stylesheets/controllers/admin.css.scss +14 -0
  45. data/assets/vendor/bootstrap-datetimepicker.css.scss +1 -0
  46. data/assets/vendor/bootstrap-datetimepicker.js.coffee +1 -0
  47. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css +5 -0
  48. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js +1 -0
  49. data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.ru.js +163 -0
  50. data/assets/vendor/moment.js.coffee +1 -0
  51. data/assets/vendor/momentjs/moment-with-langs.min.js +9 -0
  52. data/assets/vendor/selectize.css.scss +2 -0
  53. data/assets/vendor/selectize.js.coffee +1 -0
  54. data/assets/vendor/selectize/selectize.bootstrap3.css +385 -0
  55. data/assets/vendor/selectize/selectize.css +311 -0
  56. data/assets/vendor/selectize/selectize.default.css +381 -0
  57. data/assets/vendor/selectize/selectize.js +3345 -0
  58. data/assets/vendor/smart-list-table.css.scss +42 -0
  59. data/assets/vendor/smart-list-table.js.coffee +1 -0
  60. data/assets/vendor/smart-list-table/smart-list-table-row.js.coffee +63 -0
  61. data/assets/vendor/smart-list-table/smart-list-table.css.scss +54 -0
  62. data/assets/vendor/smart-list-table/smart-list-table.js.coffee +133 -0
  63. data/assets/vendor/smart-tree-table.css.scss +42 -0
  64. data/assets/vendor/smart-tree-table.js.coffee +1 -0
  65. data/assets/vendor/smart-tree-table/smart-tree-table-drag-n-drop.js.coffee +190 -0
  66. data/assets/vendor/smart-tree-table/smart-tree-table-row.js.coffee +78 -0
  67. data/assets/vendor/smart-tree-table/smart-tree-table.css.scss +54 -0
  68. data/assets/vendor/smart-tree-table/smart-tree-table.js.coffee +267 -0
  69. data/assets/vendor/spin.js +353 -0
  70. data/config/README.md +3 -0
  71. data/config/development/.keep +0 -0
  72. data/config/production/.keep +0 -0
  73. data/db/model/README.md +1 -0
  74. data/db/model/admin/user_new_form.rb +40 -0
  75. data/db/model/user.rb +26 -0
  76. data/db/seed/01_admin_roles.seed +8 -0
  77. data/db/seed/02_admin_access.seed +24 -0
  78. data/db/seed/development/.keep +0 -0
  79. data/db/seed/development/20_users.seed +45 -0
  80. data/db/seed/development/admin_users.seed +38 -0
  81. data/db/seed/production/.keep +0 -0
  82. data/lib/aerogel/admin.rb +25 -0
  83. data/lib/aerogel/admin/core.rb +14 -0
  84. data/lib/aerogel/admin/menu.rb +38 -0
  85. data/lib/aerogel/admin/table_builder.rb +100 -0
  86. data/lib/aerogel/admin/tabs_builder.rb +69 -0
  87. data/lib/aerogel/admin/version.rb +5 -0
  88. data/locales/actions.en.yml +27 -0
  89. data/locales/actions.ru.yml +28 -0
  90. data/locales/admin.en.yml +14 -0
  91. data/locales/admin.ru.yml +14 -0
  92. data/locales/models.en.yml +8 -0
  93. data/locales/models.ru.yml +8 -0
  94. data/locales/views.en.yml +46 -0
  95. data/locales/views.ru.yml +46 -0
  96. data/public/README.md +1 -0
  97. data/rake/README.md +3 -0
  98. data/views/admin/index.html.erb +3 -0
  99. data/views/admin/table_builder/standard/_table_column.html.erb +3 -0
  100. data/views/admin/table_builder/standard/_table_row.html.erb +7 -0
  101. data/views/admin/table_builder/standard/table.html.erb +10 -0
  102. data/views/admin/tabs_builder/standard/_tab.html.erb +3 -0
  103. data/views/admin/tabs_builder/standard/tabs.html.erb +3 -0
  104. data/views/admin/users/_tabs.html.erb +8 -0
  105. data/views/admin/users/access/delete.html.erb +12 -0
  106. data/views/admin/users/access/edit.html.erb +9 -0
  107. data/views/admin/users/access/index.html.erb +23 -0
  108. data/views/admin/users/access/new.html.erb +9 -0
  109. data/views/admin/users/delete.html.erb +12 -0
  110. data/views/admin/users/edit.html.erb +46 -0
  111. data/views/admin/users/index.html.erb +31 -0
  112. data/views/admin/users/new.html.erb +11 -0
  113. data/views/admin/users/roles/delete.html.erb +12 -0
  114. data/views/admin/users/roles/edit.html.erb +9 -0
  115. data/views/admin/users/roles/index.html.erb +21 -0
  116. data/views/admin/users/roles/new.html.erb +7 -0
  117. data/views/form_builder/standard/field_multiselect.erb +14 -0
  118. data/views/form_builder/standard/field_select.erb +17 -0
  119. data/views/layouts/admin.html.erb +63 -0
  120. data/views/layouts/admin/_ajax_indicator.html.erb +4 -0
  121. data/views/layouts/admin/_alerts.html.erb +22 -0
  122. data/views/layouts/admin/_menu.html.erb +50 -0
  123. data/views/layouts/admin/_menu_item.html.erb +5 -0
  124. data/views/layouts/admin/_modals.html.erb +8 -0
  125. data/views/layouts/admin/modal.html.erb +38 -0
  126. metadata +280 -0
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Generated by IcoMoon</metadata>
5
+ <defs>
6
+ <font id="aerogel-icons" horiz-adv-x="512">
7
+ <font-face units-per-em="512" ascent="480" descent="-32" />
8
+ <missing-glyph horiz-adv-x="512" />
9
+ <glyph unicode="&#x20;" d="" horiz-adv-x="256" />
10
+ <glyph unicode="&#xe001;" d="M19 475.5h474q5.5 0 9.5-4t4-9.5v-474q0-5.5-4-9.5t-9.5-4h-474q-5.5 0-9.5 4t-4 9.5v474q0 5.5 4 9.5t9.5 4zM256 232l228.5 228.5h-457zM491.5-4v457.5l-228.5-228.5zM20.5 453.5v-457l228.5 228.5zM27.5-10.5h456.5l-228 228.5z" />
11
+ </font></defs></svg>
File without changes
@@ -0,0 +1,7 @@
1
+ #= require utils/form-data-async
2
+ #= require_tree ./admin-modal
3
+
4
+ $ ->
5
+ # console?.log "/admin/admin-modal enabling async forms: data-target: .modal-content"
6
+ form_data_async_enable $(".modal-content FORM"), ".modal-content"
7
+ console?.log "** admin/modal loaded"
@@ -0,0 +1,56 @@
1
+ #
2
+ # Move 'submit' and 'cancel' buttons from Bootstrap modal's body to footer
3
+ #
4
+
5
+ admin_modal_replace_buttons = (el, target) ->
6
+ # console?.log "Moving buttons around: #{$(el).size()}"
7
+ $(el).each ->
8
+ button = $(this)
9
+ form = button.closest "form"
10
+ target = form.closest('.modal-content').find target
11
+ if button.attr('type') == 'submit'
12
+ admin_modal_replace_submit_button button, form, target
13
+ if button.attr('type') == 'cancel'
14
+ admin_modal_replace_cancel_button button, form, target
15
+ if button.attr('type') == 'select'
16
+ admin_modal_replace_other_button button, form, target
17
+
18
+
19
+ # Process 'submit' button
20
+ #
21
+ admin_modal_replace_submit_button = (button, form, target) ->
22
+ button.click ->
23
+ form.submit()
24
+ button.appendTo target
25
+ enable_form_submit_on_enter form
26
+
27
+ # Process 'cancel' button
28
+ #
29
+ admin_modal_replace_cancel_button = (button, form, target) ->
30
+ button.attr 'data-dismiss', 'modal'
31
+ button.appendTo target
32
+
33
+ # Process 'cancel' button
34
+ #
35
+ admin_modal_replace_other_button = (button, form, target) ->
36
+ # button.attr 'data-dismiss', 'modal'
37
+ button.appendTo target
38
+
39
+
40
+
41
+ enable_form_submit_on_enter = (form) ->
42
+ unless form.attr 'form-submit-on-enter-enabled'
43
+ form.keypress (e) ->
44
+ if e.keyCode == 13 && ( e.target.type != 'textarea' )
45
+ e.preventDefault()
46
+ $(this).submit()
47
+ form.attr 'form-submit-on-enter-enabled', true
48
+ # console?.log "Enabled form keypress"
49
+
50
+
51
+ $ ->
52
+ admin_modal_replace_buttons(
53
+ ".modal-body .btn[type=submit], .modal-body .btn[type=cancel], .modal-body .btn[type=select]",
54
+ ".modal-footer"
55
+ )
56
+
@@ -0,0 +1,13 @@
1
+ #= require bootstrap
2
+ #= require utils/bootstrap-modal-reload
3
+ #= require utils/ajax-watcher
4
+ #= require utils/ajax-spinner
5
+ #= require utils/on-future-elements
6
+ #= require utils/i18n
7
+ #= require spin
8
+ #= require selectize
9
+
10
+
11
+ #= require_tree ./admin
12
+
13
+ console?.log "** admin/ loaded"
@@ -0,0 +1,7 @@
1
+ $ ->
2
+ # console?.log "** selectize-inputs: initialized"
3
+
4
+ on_future_elements 'select.selectize:not(.selectize-applied)', (e) ->
5
+ console?.log "** selectize-inputs: element observed:", $(e).get(0).tagName
6
+ $(e).addClass('selectize-applied')
7
+ $(e).selectize();
@@ -0,0 +1,9 @@
1
+
2
+ $ ->
3
+ $(window).scroll ->
4
+ y = $(window).scrollTop()
5
+ if y > 0
6
+ $("#top-menu").addClass "has-shadow"
7
+ else
8
+ $("#top-menu").removeClass "has-shadow"
9
+ # console?.log "admin/top-menu-shadow loaded"
@@ -0,0 +1,26 @@
1
+ ajax_spinner = null
2
+
3
+ ajax_spinner_opts =
4
+ lines: 13 # The number of lines to draw
5
+ length: 7 # The length of each line
6
+ width: 3 # The line thickness
7
+ radius: 10 # The radius of the inner circle
8
+ corners: 1 # Corner roundness (0..1)
9
+ rotate: 0 # The rotation offset
10
+ direction: 1 # 1: clockwise, -1: counterclockwise
11
+ color: '#fff' # #rgb or #rrggbb or array of colors
12
+ speed: 1 # Rounds per second
13
+ trail: 60 # Afterglow percentage
14
+ shadow: false # Whether to render a shadow
15
+ hwaccel: false # Whether to use hardware acceleration
16
+
17
+ # Shows ajax spinner in a +target+ element specified by selector.
18
+ #
19
+ ( exports ? this ).ajax_spinner_show = (target) ->
20
+ target = $(target).get( 0 )
21
+ ajax_spinner = new Spinner( ajax_spinner_opts ).spin target
22
+
23
+ # Hides current ajax spinner.
24
+ #
25
+ ( exports ? this ).ajax_spinner_hide = ->
26
+ ajax_spinner.stop()
@@ -0,0 +1,18 @@
1
+
2
+ show_global_ajax_indicator = ->
3
+ $("#ajax-indicator").show()
4
+ ajax_spinner_show "#ajax-indicator .ajax-indicator-inner"
5
+
6
+ hide_global_ajax_indicator = ->
7
+ ajax_spinner_hide()
8
+ $("#ajax-indicator").hide()
9
+
10
+
11
+ $ ->
12
+ $(document).bind "ajaxStart", ->
13
+ console?.log "** ajax-watcher: start"
14
+ show_global_ajax_indicator()
15
+ $(document).bind "ajaxStop", ->
16
+ console?.log "** ajax-watcher: stop"
17
+ hide_global_ajax_indicator()
18
+ # console?.log "** ajax-watcher: initialized"
@@ -0,0 +1,8 @@
1
+ # Forces to reload Bootstrap modal on each access.
2
+ # Otherwise the modal contains the same content after it is invoked once.
3
+ #
4
+ $ ->
5
+ $(document).on 'hidden.bs.modal', (e) ->
6
+ # console?.log "bootstrap modal hidden:", e.target
7
+ $(e.target).removeData 'bs.modal'
8
+ $(e.target).find(".modal-content").empty()
@@ -0,0 +1,27 @@
1
+ # Redefines submit behaviour for forms specified by +el+ selector.
2
+ #
3
+ # On submit handler composes and sends AJAX request, renders result
4
+ # in an element specified by form's +target+ attribute.
5
+ #
6
+ ( exports ? this ).form_data_async_enable = (el, target) ->
7
+ $(el).on 'submit', (event)->
8
+ form = $(this)
9
+ $.ajax
10
+ type: form.attr 'method'
11
+ url: form.attr 'action'
12
+ data: form.serialize()
13
+ error: (x, e, status ) ->
14
+ console?.log "form-data-async submit error: #{e}, status: #{status}"
15
+ success: (data, status, xhr) ->
16
+ content_type = xhr.getResponseHeader 'content-type'
17
+ if /^text\/html/.test content_type
18
+ $(target).empty()
19
+ $(target).html data
20
+ else
21
+ console?.log "form-data-async ignoring content: #{content_type}"
22
+
23
+ event.preventDefault()
24
+ console?.log "form-data-async enabled for #{el}, target:#{target}"
25
+
26
+ $ ->
27
+ console?.log "form-data-async loaded"
@@ -0,0 +1,27 @@
1
+ #
2
+ log = (msg) ->
3
+ console?.log "** admin/i18n: #{msg}"
4
+
5
+
6
+ class I18nProcessor
7
+ # @default_locale: 'en'
8
+
9
+ constructor: ( @default_locale = 'en', @options = {} ) ->
10
+ # ...
11
+ log "locale processor initialized: #{@default_locale}"
12
+ @set @default_locale
13
+
14
+ # Sets new locale
15
+ #
16
+ set: (locale) ->
17
+ @current_locale = locale
18
+ log "locale set to: #{locale}"
19
+
20
+ # Returns current locale
21
+ #
22
+ locale: ->
23
+ @current_locale
24
+
25
+ @I18n = new I18nProcessor
26
+
27
+
@@ -0,0 +1,15 @@
1
+ # Applies +func+ to elements selected by +el+ on _each_ DOM insertion.
2
+ #
3
+ # Example:
4
+ # on_future_elements "select.selectize:not(.selectize-applied)", (e) ->
5
+ # $(e).selectize()
6
+ # $(e).addClass "selectize-applied"
7
+ #
8
+ ( exports ? this ).on_future_elements = (el, func) ->
9
+ $(document).on 'DOMNodeInserted', (e) ->
10
+ $(el).each ->
11
+ func( this )
12
+
13
+
14
+ #$(document).on 'DOMNodeInserted', 'input', (e) ->
15
+ # console?.log "** select2ify: element loaded"
@@ -0,0 +1,33 @@
1
+
2
+ // This is the place for customizing your bootstrap installation
3
+ //
4
+
5
+ $component-active-bg: $item-selected-bg;
6
+ $component-active-fg: $item-selected-fg;
7
+
8
+ // modals
9
+ $modal-title-padding: 7px;
10
+ $modal-inner-padding: 7px;
11
+ $modal-backdrop-bg: rgba(0,0,0,0.3); // #fff;
12
+ // $modal-header-bg: #eee;
13
+ $modal-footer-bg: #eee;
14
+ $modal-header-border-color: transparent;
15
+ $modal-footer-border-color: #ccc;
16
+ $modal-content-border-color: #888;
17
+ $modal-vertical-pos: 30%;
18
+ $modal-radius: 6px;
19
+
20
+ // navbar settings
21
+ $navbar-height: $header-height;
22
+ $navbar-inverse-bg: $top-menu-bg;
23
+ $navbar-inverse-link-color: $top-menu-fg;
24
+
25
+
26
+ // base colors:
27
+ $brand-danger: $item-with-error-fg;
28
+
29
+ // inputs
30
+ $input-color-placeholder: #ddd;
31
+ $input-border-focus: $input-border-focus-color;
32
+ // $input-border-radius:
33
+ $input-border: $input-border-color;
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Global styles
3
+ */
4
+ $body-bg: white;
5
+ $text-color: black;
6
+ $text-disabled-color: #d8d8d8;
7
+ $text-secondary-color: #d8d8d8;
8
+ $link-color: #9ac2e7;
9
+ $link-hover-color: darken($link-color, 15%);
10
+ $link-js-color: #d8d8d8;
11
+ $link-js-hover-color: darken($link-js-color, 30%);
12
+
13
+ $item-selected-bg: #428bca;
14
+ $item-selected-fg: white;
15
+ $item-hover-bg: #f5f5f5;
16
+ $item-hover-fg: inherit;
17
+ $item-disabled-fg: #afafaf;
18
+ $item-disabled-bg: inherit;
19
+ $item-nodata-fg: #d8d8d8;
20
+ $item-nodata-bg: inherit;
21
+ $item-with-error-fg: #a94442;
22
+ $item-with-error-bg: inherit;
23
+
24
+ /*
25
+ * Inputs styling
26
+ */
27
+ $input-placeholder-fg: #ddd;
28
+ $input-border-focus-color: #66afe9;
29
+ $input-border-radius: 3px;
30
+ $input-border-color: #d8d8d8;
31
+
32
+ /*
33
+ * Grid layout main column width
34
+ */
35
+ $layout-width: 800px;
36
+
37
+
38
+ /*
39
+ * Header settings
40
+ */
41
+ $header-height: 40px;
42
+ $top-menu-bg: #4a484c;
43
+ $top-menu-fg: #e4e4e4;
44
+
45
+ /*
46
+ * Footer settings
47
+ */
48
+ $footer-bg: #4a484c;
49
+ $footer-fg: #848484;
50
+ $footer-link-color: #9ac2e7;
51
+ $footer-height: 50px;
52
+
53
+
54
+ // Smart Tree Table settings
55
+ $stt-selected-bg: $item-selected-bg;
56
+ $stt-selected-fg: $item-selected-fg;
57
+ $stt-hover-bg: $item-hover-bg;
58
+ $stt-hover-fg: $item-hover-fg;
59
+ $stt-drag-over-bg: $item-hover-bg;
60
+ $stt-drag-over-fg: $item-hover-fg;
61
+
@@ -0,0 +1,26 @@
1
+ #ajax-indicator {
2
+ display: none;
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ right: 0;
7
+ bottom: 0;
8
+ z-index: 2000;
9
+ // width: 50%;
10
+ // height: 50%;
11
+
12
+ .ajax-indicator-inner {
13
+ // font-size: 36px;
14
+ height: 72px;
15
+ width: 72px;
16
+ background: #111;
17
+ opacity: 0.7;
18
+ color: white;
19
+ text-align: center;
20
+ border-radius: 5px 5px 5px 5px;
21
+ @extend .center-absolutely;
22
+ // top: 50%;
23
+
24
+ }
25
+ }
26
+
@@ -0,0 +1,24 @@
1
+ $modal-header-bg: transparent !default;
2
+ $modal-footer-bg: transparent !default;
3
+
4
+
5
+ div.modal-header {
6
+ background-color: $modal-header-bg;
7
+ border-radius: $modal-radius $modal-radius 0 0;
8
+ }
9
+ div.modal-footer {
10
+ background-color: $modal-footer-bg;
11
+ border-radius: 0 0 $modal-radius $modal-radius;
12
+ margin-top: 0;
13
+ }
14
+
15
+ $modal-vertical-pos: 50% !default;
16
+ .modal-dialog {
17
+ // margin-top:$modal-vertical-pos;
18
+ }
19
+ .modal-content {
20
+ // margin-top:-$modal-vertical-pos;
21
+
22
+ // FIXME get rid of border around empty modal during loading of remote content
23
+ border: none;
24
+ }
@@ -0,0 +1,5 @@
1
+ .language-selector {
2
+ .dropdown-menu {
3
+ min-width: 0px !important;
4
+ }
5
+ }
@@ -0,0 +1,7 @@
1
+ .page-header {
2
+ margin: 0 0 10px;
3
+ padding-bottom: 0;
4
+ h1 {
5
+ margin: 10px 0 10px;
6
+ }
7
+ }
@@ -0,0 +1,34 @@
1
+ /* Sticky footer styles
2
+ -------------------------------------------------- */
3
+ html,
4
+ body {min-height:100%; padding:0; margin:0;}
5
+ body {
6
+ padding: $header-height 0 $footer-height 0;
7
+ position:absolute;
8
+ top:0; bottom:0; left:0; right:0;
9
+ }
10
+
11
+ #page-content {
12
+ min-height:100%;
13
+ padding: $padding-large-vertical 15px $padding-large-vertical;
14
+ }
15
+
16
+
17
+ #footer {
18
+ margin-bottom: -$footer-height;
19
+ height: $footer-height;
20
+ width: 100%;
21
+ color: $footer-fg;
22
+ background-color: $footer-bg;
23
+
24
+ #footer-contents {
25
+ padding-right: 15px;
26
+ padding-left: 15px;
27
+ vertical-align: middle;
28
+ line-height: $footer-height;
29
+ A {
30
+ color: $footer-link-color;
31
+ }
32
+ }
33
+ }
34
+