bhf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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}}