bhf 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 (63) hide show
  1. data/README.md +0 -0
  2. data/app/controllers/bhf/application_controller.rb +77 -0
  3. data/app/controllers/bhf/entries_controller.rb +123 -0
  4. data/app/controllers/bhf/pages_controller.rb +49 -0
  5. data/app/helpers/bhf/application_helper.rb +22 -0
  6. data/app/helpers/bhf/entries_helper.rb +16 -0
  7. data/app/helpers/bhf/pages_helper.rb +30 -0
  8. data/app/views/bhf/_footer.haml +11 -0
  9. data/app/views/bhf/application/index.haml +3 -0
  10. data/app/views/bhf/entries/_form.haml +14 -0
  11. data/app/views/bhf/entries/_validation_errors.haml +5 -0
  12. data/app/views/bhf/entries/edit.haml +2 -0
  13. data/app/views/bhf/entries/form/belongs_to/_radio.haml +7 -0
  14. data/app/views/bhf/entries/form/belongs_to/_select.haml +6 -0
  15. data/app/views/bhf/entries/form/belongs_to/_static.haml +9 -0
  16. data/app/views/bhf/entries/form/column/_boolean.haml +2 -0
  17. data/app/views/bhf/entries/form/column/_date.haml +2 -0
  18. data/app/views/bhf/entries/form/column/_file.haml +2 -0
  19. data/app/views/bhf/entries/form/column/_markdown.haml +3 -0
  20. data/app/views/bhf/entries/form/column/_number.haml +2 -0
  21. data/app/views/bhf/entries/form/column/_static.haml +8 -0
  22. data/app/views/bhf/entries/form/column/_string.haml +2 -0
  23. data/app/views/bhf/entries/form/column/_text.haml +2 -0
  24. data/app/views/bhf/entries/form/column/_wysiwyg.haml +3 -0
  25. data/app/views/bhf/entries/form/has_and_belongs_to_many/_check_box.haml +4 -0
  26. data/app/views/bhf/entries/form/has_and_belongs_to_many/_static.haml +12 -0
  27. data/app/views/bhf/entries/form/has_many/_static.haml +12 -0
  28. data/app/views/bhf/entries/form/has_one/_static.haml +9 -0
  29. data/app/views/bhf/entries/new.haml +2 -0
  30. data/app/views/bhf/helper/_field_errors.haml +4 -0
  31. data/app/views/bhf/helper/_flash.haml +4 -0
  32. data/app/views/bhf/helper/_info.haml +2 -0
  33. data/app/views/bhf/helper/_node.haml +6 -0
  34. data/app/views/bhf/helper/_reflection_node.haml +7 -0
  35. data/app/views/bhf/pages/_platform.haml +41 -0
  36. data/app/views/bhf/pages/macro/belongs_to/_default.haml +2 -0
  37. data/app/views/bhf/pages/macro/column/_boolean.haml +1 -0
  38. data/app/views/bhf/pages/macro/column/_date.haml +1 -0
  39. data/app/views/bhf/pages/macro/column/_number.haml +1 -0
  40. data/app/views/bhf/pages/macro/column/_primary_key.haml +1 -0
  41. data/app/views/bhf/pages/macro/column/_string.haml +1 -0
  42. data/app/views/bhf/pages/macro/column/_text.haml +1 -0
  43. data/app/views/bhf/pages/macro/has_and_belongs_to_many/_default.haml +2 -0
  44. data/app/views/bhf/pages/macro/has_many/_default.haml +2 -0
  45. data/app/views/bhf/pages/macro/has_one/_default.haml +2 -0
  46. data/app/views/bhf/pages/show.haml +3 -0
  47. data/app/views/layouts/bhf/default.haml +27 -0
  48. data/app/views/layouts/bhf/quick_edit.haml +5 -0
  49. data/config/locales/en.yml +56 -0
  50. data/config/routes.rb +14 -0
  51. data/lib/bhf/active_record.rb +18 -0
  52. data/lib/bhf/data.rb +127 -0
  53. data/lib/bhf/form.rb +35 -0
  54. data/lib/bhf/pagination.rb +111 -0
  55. data/lib/bhf/platform.rb +202 -0
  56. data/lib/bhf/settings.rb +40 -0
  57. data/lib/bhf.rb +13 -0
  58. data/lib/engine.rb +19 -0
  59. data/lib/rails/generators/bhf/templates/initializer.rb +9 -0
  60. data/public/javascripts/bhf.js +0 -0
  61. data/public/stylesheets/bhf.css +1 -0
  62. data/test/test_helper.rb +55 -0
  63. metadata +169 -0
@@ -0,0 +1,56 @@
1
+ en:
2
+ bhf:
3
+ pages:
4
+ headline: Content
5
+
6
+ helpers:
7
+ searchform:
8
+ placeholder: Search
9
+ promts:
10
+ confirm: Are you sure?
11
+ entry:
12
+ new: "Create new %{platform_name}"
13
+ edit: "Edit %{platform_name}"
14
+ delete: "Delete %{platform_name}"
15
+ empty: Empty
16
+ entries:
17
+ empty: Empty
18
+ login:
19
+ success: "Login was successful"
20
+ error: "Can't login"
21
+ activated:
22
+ success: "%{model} was activated successfuly"
23
+ error: "%{model} error"
24
+ validation:
25
+ header: "%{error} prohibited this %{name} from being saved:"
26
+ error: error
27
+
28
+ quick_edit:
29
+ buttons:
30
+ cancel: Cancel
31
+ open: Open
32
+ save: Save
33
+ save_and_next: Save and go to next
34
+
35
+ pagination:
36
+ previous_label: « Previous
37
+ next_label: Next »
38
+ load_more: Show more entries
39
+ load_less: Show less entries
40
+ info:
41
+ default: "Displaying <strong>%{offset_start} - %{offset_end}</strong> of <strong>%{total_count}</strong> %{name} in total"
42
+ nothing_found: "No %{name} found"
43
+ one_found: "Displaying <strong>1</strong> %{name}"
44
+ all_displayed: "Displaying <strong>all %{total_count}</strong> %{name}"
45
+ activerecord:
46
+ notices:
47
+ messages:
48
+ create:
49
+ success: "%{model} was created successfuly"
50
+ error: "%{model} error"
51
+ update:
52
+ success: "%{model} was updated successfuly"
53
+ error: "%{model} error"
54
+ destory:
55
+ success: "%{model} was deleted successfuly"
56
+ error: "%{model} error"
data/config/routes.rb ADDED
@@ -0,0 +1,14 @@
1
+ Rails.application.routes.draw do
2
+
3
+ namespace :bhf, :path => Bhf::Engine.config.mount_at do
4
+ root :to => 'application#index'
5
+
6
+ get 'page/:page', :to => 'pages#show', :as => :page
7
+
8
+ scope ':platform' do
9
+ resources :entries, :except => [:index, :show]
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,18 @@
1
+ module Bhf
2
+ module ActiveRecord
3
+
4
+ def to_bhf_s
5
+ return title if self.respond_to? :title
6
+ return name if self.respond_to? :name
7
+
8
+ if self.respond_to? :attributes
9
+ return title if attributes['title']
10
+ return name if attributes['name']
11
+ return "#{self.class.to_s.humanize} ID: #{send(self.class.primary_key)}" if attributes[self.class.primary_key]
12
+ end
13
+
14
+ self.to_s.humanize
15
+ end
16
+
17
+ end
18
+ end
data/lib/bhf/data.rb ADDED
@@ -0,0 +1,127 @@
1
+ module Bhf
2
+ module Data
3
+
4
+ class Field
5
+
6
+ attr_reader :info
7
+
8
+ def initialize(props, options = {}, pk = 'id')
9
+ @props = props
10
+ @info = options[:info] if options[:info]
11
+
12
+ @overwrite_type = options[:overwrite_type].to_sym if options[:overwrite_type]
13
+
14
+ @primary_key = pk
15
+ end
16
+
17
+ def macro
18
+ :column
19
+ end
20
+
21
+ def type
22
+ return @overwrite_type if @overwrite_type
23
+
24
+ @props.type
25
+ end
26
+
27
+ def form_type
28
+ return @overwrite_type if @overwrite_type
29
+
30
+ if name === @primary_key || name === 'updated_at' || name === 'created_at'
31
+ :static
32
+ else
33
+ supported_types(@props.type)
34
+ end
35
+ end
36
+
37
+ def display_type
38
+ if name === @primary_key
39
+ :primary_key
40
+ elsif name === 'updated_at' || name === 'created_at'
41
+ :date
42
+ else
43
+ supported_types(@props.type)
44
+ end
45
+ end
46
+
47
+ def name
48
+ @props.name
49
+ end
50
+
51
+ private
52
+
53
+ def supported_types(check_type)
54
+ if [:boolean, :text].include?(check_type)
55
+ check_type
56
+ elsif type_sym = group_types(check_type)
57
+ type_sym
58
+ else
59
+ :string
60
+ end
61
+ end
62
+
63
+ def group_types(type_sym)
64
+ return :date if [:date, :datetime, :timestamp, :time, :year].include?(type_sym)
65
+ return :number if [:integer, :float].include?(type_sym)
66
+ return :file if type_sym === :file
67
+ end
68
+
69
+ end
70
+
71
+
72
+ class Reflection
73
+
74
+ attr_reader :reflection, :info, :link
75
+
76
+ def initialize(reflection, options = {})
77
+ @reflection = reflection
78
+ @info = options[:info] if options[:info]
79
+ @link = options[:link].to_sym if options[:link]
80
+
81
+ @overwrite_type = options[:overwrite_type].to_sym if options[:overwrite_type]
82
+ end
83
+
84
+ def macro
85
+ @reflection.macro
86
+ end
87
+
88
+ def type
89
+ return @overwrite_type if @overwrite_type
90
+
91
+ if macro === :has_and_belongs_to_many
92
+ :check_box
93
+ elsif macro === :belongs_to
94
+ :select
95
+ else
96
+ :static
97
+ end
98
+ end
99
+
100
+ def form_type
101
+ type
102
+ end
103
+
104
+ def display_type
105
+ :default
106
+ end
107
+
108
+ def name
109
+ @reflection.name.to_s
110
+ end
111
+
112
+ end
113
+
114
+
115
+ class Column
116
+
117
+ attr_reader :name, :field
118
+
119
+ def initialize(field)
120
+ @name = field.name
121
+ @field = field
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+ end
data/lib/bhf/form.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Bhf
2
+ module Form
3
+
4
+ class Builder < ActionView::Helpers::FormBuilder
5
+ include ActionView::Helpers::FormTagHelper
6
+
7
+ def error_label(name, message)
8
+ label name, "#{object.class.human_attribute_name(name)} #{message}"
9
+ end
10
+
11
+ def field_errors(field)
12
+ object.errors[field.to_sym]
13
+ end
14
+
15
+ def field_has_errors?(field)
16
+ field_errors(field).any?
17
+ end
18
+
19
+ def many_to_many_check_box(obj, ref_name, params)
20
+ mm = :has_and_belongs_to_many
21
+ checked = if params[mm] && params[mm][ref_name]
22
+ params[mm][ref_name][obj.id.to_s] != ''
23
+ else
24
+ object.send(ref_name).include?(obj)
25
+ end
26
+
27
+ hidden_field_tag("#{mm}[#{ref_name}][#{obj.id}]", '', :id => "hidden_has_and_belongs_to_many_#{ref_name}_#{obj.id}")+' '+
28
+ check_box_tag("#{mm}[#{ref_name}][#{obj.id}]", obj.id, checked)+' '+
29
+ label_tag("#{mm}_#{ref_name}_#{obj.id}", obj.to_bhf_s)
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,111 @@
1
+ module Bhf
2
+
3
+ class Pagination
4
+
5
+ attr_accessor :template
6
+ attr_reader :offset_per_page, :offset_to_add
7
+
8
+ def initialize(offset_per_page = 10, offset_to_add = 5)
9
+ @offset_per_page = offset_per_page
10
+ @offset_to_add = offset_to_add
11
+ end
12
+
13
+ def paginate(platform)
14
+ platform_params = template.params[platform.name] || {}
15
+
16
+ if page_links = template.will_paginate(platform.paginated_objects, {
17
+ :previous_label => I18n.t('bhf.pagination.previous_label'),
18
+ :next_label => I18n.t('bhf.pagination.next_label'),
19
+ :renderer => LinkRenderer.new(self, platform),
20
+ :container => false
21
+ })
22
+ links = "#{load_more(platform)} #{page_links}"
23
+ elsif platform.paginated_objects.total_pages == 1 && platform.paginated_objects.size > @offset_to_add
24
+ links = load_less(platform)
25
+ end
26
+
27
+ if links
28
+ template.content_tag(:div, links.html_safe, {:class => 'pagination'})
29
+ end
30
+ end
31
+
32
+ def info(platform, options = {})
33
+ collection = platform.paginated_objects
34
+
35
+ entry_name = options[:entry_name] ||
36
+ (collection.empty?? 'entry' : collection.first.class.model_name.human)
37
+
38
+ info = if collection.total_pages < 2
39
+ case collection.size
40
+ when 0
41
+ I18n.t 'bhf.pagination.info.nothing_found', :name => entry_name.pluralize
42
+ when 1
43
+ I18n.t 'bhf.pagination.info.one_found', :name => entry_name
44
+ else
45
+ I18n.t 'bhf.pagination.info.all_displayed', :total_count => collection.size, :name => entry_name.pluralize
46
+ end
47
+ else
48
+ I18n.t('bhf.pagination.info.default', {
49
+ :name => entry_name.pluralize,
50
+ :total_count => collection.total_entries,
51
+ :offset_start => collection.offset + 1,
52
+ :offset_end => collection.offset + collection.length
53
+ })
54
+ end
55
+
56
+ info.html_safe
57
+ end
58
+
59
+ def load_more(platform, attributes = {}, plus = true)
60
+ platform_params = template.params[platform.name] || {}
61
+ load_offset = @offset_per_page
62
+ load_offset = platform_params[:per_page].to_i if platform_params[:per_page]
63
+ if plus
64
+ load_offset += @offset_to_add
65
+ else
66
+ load_offset -= @offset_to_add
67
+ end
68
+
69
+ platform_params.delete(:page)
70
+ platform_params[:per_page] = load_offset
71
+
72
+ direction = (plus ? 'more' : 'less')
73
+ template.link_to(
74
+ I18n.t("bhf.pagination.load_#{direction}"),
75
+ template.bhf_page_path(
76
+ platform.page_name,
77
+ template.params.merge(platform.name => platform_params)
78
+ ), attributes.merge(:class => "load_#{direction}")
79
+ )
80
+ end
81
+
82
+ def load_less(platform, attributes = {})
83
+ load_more(platform, attributes, false)
84
+ end
85
+
86
+
87
+ class LinkRenderer < WillPaginate::LinkRenderer
88
+
89
+ def initialize(bhf_pagination, platform)
90
+ @b_p = bhf_pagination
91
+ @platform = platform
92
+ end
93
+
94
+ def page_link(page, text, attributes = {})
95
+ platform_params = @b_p.template.params[@platform.name] || {}
96
+ platform_params[:page] = page
97
+
98
+ @b_p.template.link_to(
99
+ text,
100
+ @b_p.template.bhf_page_path(
101
+ @platform.page_name,
102
+ @b_p.template.params.merge(@platform.name => platform_params)
103
+ ), attributes
104
+ )
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,202 @@
1
+ module Bhf
2
+ class Platform
3
+
4
+ attr_accessor :paginated_objects
5
+ attr_reader :name, :title, :page_name
6
+
7
+ def initialize(options, page_name)
8
+ @paginated_objects = []
9
+
10
+ if options.is_a?(String)
11
+ options = {options => nil}
12
+ end
13
+ @name = options.keys[0]
14
+ @data = options.values[0] || {}
15
+ @collection = get_collection
16
+
17
+ human_title = if model.to_s === @name.singularize.camelize
18
+ model.model_name.human
19
+ else
20
+ @name.humanize
21
+ end
22
+
23
+ @title = I18n.t("bhf.platforms.#{@name}.title", :platform_title => human_title, :default => human_title).pluralize
24
+ @page_name = page_name
25
+ end
26
+
27
+ def search?
28
+ table_options(:search) != false
29
+ end
30
+
31
+ def custom_columns?
32
+ table_options(:columns).is_a?(Array)
33
+ end
34
+
35
+ def search
36
+ table_options(:search) || :where
37
+ end
38
+
39
+ def prepare_objects(options)
40
+ chain = model
41
+
42
+ if options[:order]
43
+ chain = chain.order("#{options[:order]} #{options[:direction]}")
44
+ end
45
+
46
+ if search? && options[:search].present?
47
+ chain = do_search(chain, options[:search])
48
+ end
49
+
50
+ @objects = chain.send(data_source)
51
+ end
52
+
53
+ def model
54
+ return @data['model'].constantize if @data['model']
55
+ @name.singularize.camelize.constantize
56
+ end
57
+
58
+ def model_name
59
+ ActiveModel::Naming.singular(model)
60
+ end
61
+
62
+ def fields
63
+ default_attrs(form_options(:display))
64
+ end
65
+
66
+ def columns
67
+ default_attrs(table_options(:columns)).
68
+ each_with_object([]) do |field, obj|
69
+ obj << Bhf::Data::Column.new(field)
70
+ end
71
+ end
72
+
73
+ def has_file_upload?
74
+ @collection.each do |field|
75
+ return true if field.form_type === :file
76
+ end
77
+ return false
78
+ end
79
+
80
+ def table
81
+ @data['table']
82
+ end
83
+
84
+ def form
85
+ @data['form']
86
+ end
87
+
88
+ def hooks(method)
89
+ @data['hooks'][method.to_s] if @data['hooks']
90
+ end
91
+
92
+ private
93
+
94
+ def do_search(chain, search_term)
95
+ search_condition = if table_options(:search)
96
+ search_term
97
+ else
98
+ where_statement = []
99
+ model.columns_hash.each_pair do |name, props|
100
+ if props.type === :string
101
+ where_statement << "#{name} LIKE '%#{search_term}%'"
102
+ elsif props.type === :integer
103
+ where_statement << "#{name} = #{search_term.to_i}"
104
+ elsif props.type === :float
105
+ where_statement << "#{name} = #{search_term.to_f}"
106
+ end
107
+ end
108
+
109
+ where_statement.join(' OR ')
110
+ end
111
+
112
+ chain.send search, search_condition
113
+ end
114
+
115
+ def data_source
116
+ table_options(:source) || :all
117
+ end
118
+
119
+ def default_attrs(attrs)
120
+ if attrs
121
+ model_respond_to?(attrs)
122
+ @collection.select do |field|
123
+ attrs.include?(field.name)
124
+ end
125
+ else
126
+ @collection
127
+ end
128
+ end
129
+
130
+ def get_collection
131
+ all = {}
132
+
133
+ model.columns_hash.each_pair do |name, props|
134
+ all[name] = Bhf::Data::Field.new(props, {
135
+ :overwrite_type => form_options(:types, name),
136
+ :info => I18n.t("bhf.platforms.#{@name}.infos.#{name}", :default => '')
137
+ }, model.primary_key)
138
+ end
139
+
140
+ model.reflections.each_pair do |name, props|
141
+ all[name.to_s] = Bhf::Data::Reflection.new(props, {
142
+ :overwrite_type => form_options(:types, name),
143
+ :info => I18n.t("bhf.platforms.#{@name}.infos.#{name}", :default => ''),
144
+ :link => form_options(:links, name)
145
+ })
146
+
147
+ fk = all[name.to_s].reflection.association_foreign_key
148
+ if all.has_key?(fk)
149
+ all.delete(fk)
150
+ end
151
+ end
152
+
153
+ default_sort(all)
154
+ end
155
+
156
+ def default_sort(attrs)
157
+ id = []
158
+ static_dates = []
159
+ output = []
160
+
161
+ attrs.each_pair do |key, value|
162
+ if key === model.primary_key
163
+ id << value
164
+ elsif key === 'created_at' || key === 'updated_at'
165
+ static_dates << value
166
+ else
167
+ output << value
168
+ end
169
+ end
170
+
171
+ id + output.sort_by(&:name) + static_dates
172
+ end
173
+
174
+ def model_respond_to?(field_names)
175
+ new_obj = model.new
176
+ field_names.each do |field_name|
177
+ unless new_obj.respond_to?(field_name)
178
+ raise Exception.new("Model '#{model}' does not respond to '#{field_name}'")
179
+ return false
180
+ end
181
+ end
182
+ true
183
+ end
184
+
185
+ def form_options(key, attribute = nil)
186
+ if form
187
+ if attribute === nil
188
+ form[key.to_s]
189
+ elsif form[key.to_s]
190
+ form[key.to_s][attribute.to_s]
191
+ end
192
+ end
193
+ end
194
+
195
+ def table_options(key)
196
+ if table
197
+ return table[key.to_s]
198
+ end
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,40 @@
1
+ module Bhf
2
+
3
+ class Settings
4
+
5
+ def initialize(options)
6
+ @options = options
7
+ end
8
+
9
+ def pages
10
+ @options['pages'].each_with_object([]) do |page, obj|
11
+ if page.is_a?(String)
12
+ page = {page => nil}
13
+ end
14
+ obj << page.keys[0]
15
+ end
16
+ end
17
+
18
+ def content_for_page(selected_page)
19
+ @options['pages'].each do |page|
20
+ if page.is_a?(String)
21
+ page = {page => nil}
22
+ end
23
+ if selected_page == page.keys[0]
24
+ return page.values.flatten
25
+ end
26
+ end
27
+ end
28
+
29
+ def find_platform(platform_name)
30
+ pages.each do |page|
31
+ content_for_page(page).each do |platform|
32
+ bhf_platform = Bhf::Platform.new(platform, page)
33
+ return bhf_platform if bhf_platform.name == platform_name
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
data/lib/bhf.rb ADDED
@@ -0,0 +1,13 @@
1
+ def d(var)
2
+ raise var.inspect.split(', ').join(", \n")
3
+ end
4
+
5
+ require 'engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
6
+ require 'bhf/active_record'
7
+ require 'bhf/data'
8
+ require 'bhf/platform'
9
+ require 'bhf/settings'
10
+ require 'bhf/pagination'
11
+ require 'bhf/form'
12
+
13
+ ::ActiveRecord::Base.send :include, Bhf::ActiveRecord
data/lib/engine.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'haml'
2
+ require 'will_paginate'
3
+
4
+ module Bhf
5
+ class Engine < Rails::Engine
6
+
7
+ # Config defaults
8
+ config.page_title = nil
9
+ config.mount_at = 'bhf'
10
+ config.auth_logic_from = 'ApplicationController'
11
+
12
+ # config.bhf_logic = YAML::load(IO.read('config/bhf.yml'))
13
+
14
+ initializer 'static assets' do |app|
15
+ app.middleware.use ::ActionDispatch::Static, "#{root}/public"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Bhf
2
+ class Engine < Rails::Engine
3
+
4
+ config.page_title = 'Bahhof Admin'
5
+ config.mount_at = '/bhf'
6
+ # config.auth_logic_from = 'ApplicationController'
7
+
8
+ end
9
+ end
File without changes
@@ -0,0 +1 @@
1
+ html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}ins{text-decoration:none}del{text-decoration:line-through}table{border-spacing:0}textarea,input,button,body{font:14px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif}a:focus{outline:1px dotted}hr{border:0 #C8C8C8 solid;border-top-width:1px;clear:both;height:0;margin-bottom:20px}h1{font-size:25px}h2{font-size:23px}h3{font-size:21px}h4{font-size:19px}h5{font-size:17px}h6{font-size:15px}ol{list-style:decimal}html{min-width:700px}body{background:url(/images/bg.png);margin-right:-40px;overflow-x:hidden}a{text-decoration:none;color:inherit}header{display:block;overflow:hidden;clear:both;padding:20px 20px 0 20px}header h1{float:left;display:none}header li{float:left;font-weight:bold;margin-right:30px}header li.active a{text-shadow:#fff 0px -1px 0px;background:#d5d5d5;background:-moz-linear-gradient(top, #d5d5d5, #fff);background:-webkit-gradient(linear, left top, left bottom, from(#d5d5d5), to(#fff));box-shadow:0px 1px 4px #000;-moz-box-shadow:0px 1px 4px #000;-webkit-box-shadow:0px 1px 4px #000;color:#414756}header li.active a:hover{background:#fff;background:-moz-linear-gradient(top, #fff, #d5d5d5);background:-webkit-gradient(linear, left top, left bottom, from(#fff), to(#d5d5d5));border-color:#fff;text-shadow:#fff 0px 1px 0px;box-shadow:inset 0px 0px 3px #fff;-moz-box-shadow:inset 0px 0px 3px #fff;-webkit-box-shadow:inset 0px 0px 3px #fff}header li a{text-decoration:none;display:block;padding:5px 20px;border:1px solid transparent;color:#d8d8d8;text-shadow:#000 0px -1px 0px}header li a:hover{border-color:#2b303c;box-shadow:inset 0px 0px 35px #2b303c;-moz-box-shadow:inset 0px 0px 35px #2b303c;-webkit-box-shadow:inset 0px 0px 35px #2b303c}#content{display:table;width:100%;box-sizing:border-box;margin-left:-20px;border-spacing:20px}.quick_edit_holder,#main{display:table-cell;background:#eaeaea;border:3px solid #fff;padding:10px 20px;box-shadow:0px 1px 4px rgba(0,0,0,0.5);-moz-box-shadow:0px 1px 4px rgba(0,0,0,0.5);-webkit-box-shadow:0px 1px 4px rgba(0,0,0,0.5)}#content #main{border-right:none;border-left:none}#content .quick_edit_holder{border-right:none;position:relative;width:400px}#content .quick_edit_holder .node,#content .quick_edit_holder .input,#content .quick_edit_holder .label{display:block}#content .quick_edit_holder .input{padding-top:0}#content .quick_edit_holder textarea{width:300px;height:100px}#content .quick_edit_holder > .button.cancel,#content .quick_edit_holder > .cancel.alt_button,#content .quick_edit_holder > .pagination a.cancel,.pagination #content .quick_edit_holder > a.cancel,#content .quick_edit_holder > .pagination span.cancel,.pagination #content .quick_edit_holder > span.cancel,#content .quick_edit_holder > .pagination em.cancel,.pagination #content .quick_edit_holder > em.cancel,#content .quick_edit_holder > .button.open,#content .quick_edit_holder > .open.alt_button,#content .quick_edit_holder > .pagination a.open,.pagination #content .quick_edit_holder > a.open,#content .quick_edit_holder > .pagination span.open,.pagination #content .quick_edit_holder > span.open,#content .quick_edit_holder > .pagination em.open,.pagination #content .quick_edit_holder > em.open{float:right}#content .quick_edit_holder > .button.open,#content .quick_edit_holder > .open.alt_button,#content .quick_edit_holder > .pagination a.open,.pagination #content .quick_edit_holder > a.open,#content .quick_edit_holder > .pagination span.open,.pagination #content .quick_edit_holder > span.open,#content .quick_edit_holder > .pagination em.open,.pagination #content .quick_edit_holder > em.open{margin-right:10px}table{margin-top:40px;width:100%}table caption{height:60px;background-image:url(/images/button_bg_2.png)}table caption h4,table caption .info{float:left}table caption .info{padding:5px 20px;text-transform:uppercase;font-size:12px}table caption .create{float:right}table caption form{float:left}table thead th{padding:4px 0;padding-left:5px;text-align:left;border-top:1px solid #fff;border-bottom:1px solid #c6c6c6;white-space:nowrap}table thead th.primary_key{text-transform:uppercase}table thead th.sorted{background:#414756}table thead th.sorted a:after,table thead th.sorted.desc:hover a:after{content:" ▼"}table thead th.sorted:hover a:after,table thead th.sorted.desc a:after{content:" ▲"}table thead th.sorted a{color:#fff;text-shadow:#000 0px 1px 0px}table tbody td{border-top:1px solid #fff;border-bottom:1px solid #c6c6c6}table tbody td.has_many,table tbody td.has_and_belongs_to_many,table tbody td.text{max-width:200px;white-space:nowrap;overflow:hidden}table tbody td.has_many a,table tbody td.has_and_belongs_to_many a,table tbody td.text a{-webkit-mask-image:-webkit-gradient(linear, left top, right top, color-stop(50%, #000), to(rgba(255,255,255,0)))}table tbody a{display:block;padding:5px 3px}table tbody a.edit,table tbody a.delete{padding:0;margin-right:5px;display:inline-block;text-indent:-9999px;width:20px;height:20px}table tbody a.edit:active,table tbody a.delete:active{background-position:0 1px}table tbody a.edit{background:url(/images/icon_edit.png)}table tbody a.edit:hover{background-image:url(/images/icon_edit_a.png)}table tbody a.delete{background:url(/images/icon_delete.png)}table tbody a.delete:hover{background-image:url(/images/icon_delete_a.png)}table tbody tr:hover td,table tbody tr.live_edit td{background:#0f8fdd;color:#fff}table tbody tr:hover td:first-child,table tbody tr.live_edit td:first-child{border-radius:0px;border-top-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius:0px;-moz-border-radius-topleft:4px;-moz-border-radius-bottomleft:4px}table tbody tr:hover td:last-child,table tbody tr.live_edit td:last-child{border-radius:0px;border-top-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius:0px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}table tbody tr.live_edit td{background:red}table tfoot td{padding-top:10px;border-top:1px solid #fff}table thead .primary_key,table thead .number,table tbody .primary_key,table tbody .number{padding-right:10px;text-align:right}table thead .primary_key,table tbody .primary_key{width:30px}table thead .action,table tbody .action{width:50px}.default_input,.search input,form .node .input input[type="password"],form .node .input input[type="text"],form .node .input textarea{border:1px solid #bbb;background:#fff;border-radius:2px;-moz-border-radius:2px;box-shadow:0px 1px 0px #fff;-moz-box-shadow:0px 1px 0px #fff;-webkit-box-shadow:0px 1px 0px #fff;padding:3px 5px 1px 5px;width:300px}.default_input:focus,.search input:focus,form .node .input input[type="password"]:focus,form .node .input input[type="text"]:focus,form .node .input textarea:focus{outline:0;transition-duration:1s;-moz-transition-duration:1s;-webkit-transition-duration:1s;-webkit-box-shadow:0 1px 12px #0f8fdd;border-color:#0f8fdd}.search input{width:200px}form .node{display:table-row}form .node.string .input,form .node.string .label,form .node.text .input,form .node.text .label,form .node.password .input,form .node.password .label{padding-top:5px}form .node.string .label label,form .node.text .label label,form .node.password .label label{padding-top:5px}form .node .label{width:200px}form .node .label label{display:block}form .node .label .field_with_errors{color:red}form .node.primary_key .label{text-transform:uppercase}form .node .input,form .node .label{padding-top:10px;vertical-align:top;display:table-cell}form .node .input textarea{width:500px;height:200px}form .node .input .errors{color:red}form .node .input .field_with_errors input{border-color:red}form .error_explanation label{color:red;cursor:pointer;display:block}form .error_explanation label:hover{color:#f90}footer{text-shadow:#000 0px -1px 0px;text-align:center;color:gray;font-size:12px;line-height:20px}footer a{text-decoration:none}footer a:hover{text-decoration:underline}input[type="submit"],button,.button,.alt_button,.pagination a,.pagination a,.pagination span,.pagination span,.pagination em,.pagination em{display:inline-block;border:0;padding:12px 50px;background:url(/images/button_bg.png);border-radius:4px;-moz-border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.5),inset 0 2px 0 rgba(255,255,255,0.3),inset 0 1px 0 rgba(0,0,0,0.9);text-decoration:none;font-weight:bold;color:#f1f1f1;font-size:13px;text-shadow:0 -1px 0 #000;cursor:pointer;transition-duration:0.8s;-moz-transition-duration:0.8s;-webkit-transition-duration:0.8s;transition-property:color;-moz-transition-property:color;-webkit-transition-property:color}input[type="submit"]:hover,button:hover,.button:hover,.alt_button:hover,.pagination a:hover,.pagination a:hover,.pagination span:hover,.pagination span:hover,.pagination em:hover,.pagination em:hover{-webkit-box-shadow:inset 0 10px 20px 5px #000,inset 0 -2px 0 rgba(255,255,255,0.5),inset 0 -1px 0 #000;color:#38a5e9;text-shadow:0 1px 0 #000}input[type="submit"]:active,button:active,.button:active,.alt_button:active,.pagination a:active,.pagination a:active,.pagination span:active,.pagination span:active,.pagination em:active,.pagination em:active{-webkit-box-shadow:inset 0 1px 6px rgba(0,0,0,0.3),inset 0 -1px 0 #0f8fdd,inset 0 -2px 0 rgba(255,255,255,0.6);background:url(/images/button_bg_a.png);color:#fff}input[type="submit"]:focus,button:focus,.button:focus,.alt_button:focus,.pagination a:focus,.pagination a:focus,.pagination span:focus,.pagination span:focus,.pagination em:focus,.pagination em:focus{outline:0;padding:10px 48px;transition-duration:1s;-moz-transition-duration:1s;-webkit-transition-duration:1s;transition-property:-webkit-box-shadow;-moz-transition-property:-webkit-box-shadow;-webkit-transition-property:-webkit-box-shadow;-webkit-box-shadow:0 1px 12px #0f8fdd;border:2px solid #0f8fdd}.alt_button,.pagination a,.pagination span,.pagination em{background-image:url(/images/button_bg_2.png);padding:5px 10px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.7);color:#000;text-shadow:#fff 0px 1px 0px}@media (max-width: 800px){body{overflow-x:auto}}@media (max-width: 960px){.default_columns thead th:nth-last-child(2),.default_columns tbody td:nth-last-child(2),.default_columns thead th:nth-last-child(3),.default_columns tbody td:nth-last-child(3){display:none}}