para 0.6.9 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/para/admin/tangram-mosaic.png +0 -0
  3. data/app/assets/images/para/admin/template-section.svg +23 -0
  4. data/app/assets/javascripts/para/admin.coffee +20 -0
  5. data/app/assets/javascripts/para/admin/tabs.coffee +22 -5
  6. data/app/assets/javascripts/para/admin/theme_actions.coffee +3 -3
  7. data/app/assets/javascripts/para/inputs/material-input.coffee +4 -0
  8. data/app/assets/javascripts/para/inputs/multi-select-input.coffee +173 -0
  9. data/app/assets/javascripts/para/inputs/nested_many.coffee +9 -9
  10. data/app/assets/stylesheets/para/admin.sass +5 -5
  11. data/app/assets/stylesheets/para/admin/main.sass +32 -0
  12. data/app/assets/stylesheets/para/admin/src/_alert.sass +17 -0
  13. data/app/assets/stylesheets/para/admin/src/_base.sass +51 -0
  14. data/app/assets/stylesheets/para/admin/src/_bootstrap-variables.scss +874 -0
  15. data/app/assets/stylesheets/para/admin/src/_breadcrumb.sass +22 -0
  16. data/app/assets/stylesheets/para/admin/src/_buttons.sass +107 -0
  17. data/app/assets/stylesheets/para/admin/src/_checkable.sass +94 -0
  18. data/app/assets/stylesheets/para/admin/src/_common.sass +203 -0
  19. data/app/assets/stylesheets/para/admin/src/_dropdown.sass +40 -0
  20. data/app/assets/stylesheets/para/admin/src/_form.sass +271 -0
  21. data/app/assets/stylesheets/para/admin/src/_list.sass +61 -0
  22. data/app/assets/stylesheets/para/admin/src/_mixins.sass +113 -0
  23. data/app/assets/stylesheets/para/admin/src/_multi-select.sass +91 -0
  24. data/app/assets/stylesheets/para/admin/src/_navigation.sass +150 -0
  25. data/app/assets/stylesheets/para/admin/src/_navtabs.sass +41 -0
  26. data/app/assets/stylesheets/para/admin/src/_nested-many.sass +69 -0
  27. data/app/assets/stylesheets/para/admin/src/_nested_one.sass +13 -0
  28. data/app/assets/stylesheets/para/admin/src/_orderable.sass +44 -0
  29. data/app/assets/stylesheets/para/admin/src/_page-loading.sass +39 -0
  30. data/app/assets/stylesheets/para/admin/src/_pagination.sass +34 -0
  31. data/app/assets/stylesheets/para/admin/src/_panel.sass +55 -0
  32. data/app/assets/stylesheets/para/admin/src/_responsive.sass +100 -0
  33. data/app/assets/stylesheets/para/admin/{theme → src}/_sorting.sass +11 -3
  34. data/app/assets/stylesheets/para/admin/src/_statcard.sass +41 -0
  35. data/app/assets/stylesheets/para/admin/src/_table.sass +33 -0
  36. data/app/assets/stylesheets/para/admin/src/_tree.sass +52 -0
  37. data/app/assets/stylesheets/para/admin/src/_variables.sass +37 -0
  38. data/app/assets/stylesheets/para/admin/src/_well.sass +12 -0
  39. data/app/assets/stylesheets/para/{admin/src → lib}/datetimepicker.sass +1 -3
  40. data/app/assets/stylesheets/para/{admin/src → lib}/fuelux.sass +3 -5
  41. data/app/assets/stylesheets/para/lib/jasny-bootstrap.sass +97 -0
  42. data/app/assets/stylesheets/para/{admin/src → lib}/redactor.sass +2 -5
  43. data/app/assets/stylesheets/para/lib/selectize.sass +112 -0
  44. data/app/assets/stylesheets/para/lib/slider.sass +12 -0
  45. data/app/controllers/concerns/para/admin/resource_controller_concerns.rb +14 -2
  46. data/app/controllers/para/admin/base_controller.rb +7 -0
  47. data/app/controllers/para/admin/crud_resources_controller.rb +13 -6
  48. data/app/controllers/para/admin/form_resources_controller.rb +5 -3
  49. data/app/controllers/para/admin/resources_controller.rb +9 -3
  50. data/app/controllers/para/admin/search_controller.rb +15 -0
  51. data/app/controllers/para/application_controller.rb +10 -2
  52. data/app/helpers/para/admin/page_helper.rb +9 -9
  53. data/app/helpers/para/application_helper.rb +1 -10
  54. data/app/helpers/para/form_helper.rb +5 -6
  55. data/app/helpers/para/model_helper.rb +13 -3
  56. data/app/helpers/para/ordering_helper.rb +1 -1
  57. data/app/helpers/para/search_results_helper.rb +16 -0
  58. data/app/helpers/para/tree_helper.rb +10 -6
  59. data/app/models/para/page.rb +5 -0
  60. data/app/models/para/page/section.rb +42 -0
  61. data/app/models/para/page/section_resource.rb +12 -0
  62. data/app/views/layouts/para/admin.html.haml +5 -8
  63. data/app/views/para/admin/dashboard.html.haml +21 -8
  64. data/app/views/para/admin/form_resources/show.html.haml +1 -1
  65. data/app/views/para/admin/imports/new.html.haml +2 -2
  66. data/app/views/para/admin/resources/_actions.html.haml +3 -3
  67. data/app/views/para/admin/resources/_add_button.html.haml +1 -1
  68. data/app/views/para/admin/resources/_exports_menu.html.haml +6 -6
  69. data/app/views/para/admin/resources/_filters.html.haml +7 -8
  70. data/app/views/para/admin/resources/_imports_menu.html.haml +6 -6
  71. data/app/views/para/admin/resources/_list.html.haml +14 -15
  72. data/app/views/para/admin/resources/_per_page_select.html.haml +4 -3
  73. data/app/views/para/admin/resources/_subclassable_add_button.html.haml +3 -3
  74. data/app/views/para/admin/resources/_tree.html.haml +7 -9
  75. data/app/views/para/admin/resources/_tree_item.html.haml +5 -6
  76. data/app/views/para/admin/resources/edit.html.haml +1 -1
  77. data/app/views/para/admin/resources/new.html.haml +1 -1
  78. data/app/views/para/admin/search/_result.html.haml +7 -0
  79. data/app/views/para/admin/search/index.html.haml +1 -0
  80. data/app/views/para/admin/settings_component/show.html.haml +1 -1
  81. data/app/views/para/admin/shared/_breadcrumbs.html.haml +2 -3
  82. data/app/views/para/admin/shared/_header.html.haml +16 -27
  83. data/app/views/para/admin/shared/_navigation.html.haml +20 -19
  84. data/app/views/para/form/_tabs.html.haml +6 -7
  85. data/app/views/para/inputs/_multi_select.html.haml +40 -0
  86. data/app/views/para/inputs/_nested_many.html.haml +4 -4
  87. data/app/views/para/inputs/multi_select/_no_items.html.haml +6 -0
  88. data/app/views/para/inputs/nested_many/_add.html.haml +1 -2
  89. data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +4 -5
  90. data/app/views/para/inputs/nested_many/_container.html.haml +7 -8
  91. data/app/views/shared/_breadcrumbs.html.haml +4 -0
  92. data/config/locales/fr.yml +29 -4
  93. data/db/migrate/20170324125547_create_para_page_section_resources.rb +15 -0
  94. data/lib/generators/para/filters/templates/_filters.html.haml +3 -5
  95. data/lib/generators/para/install/templates/initializer.rb +12 -0
  96. data/lib/generators/para/page/section/section_generator.rb +13 -2
  97. data/lib/generators/para/page/section/templates/section.html.haml.erb +3 -0
  98. data/lib/generators/para/resource/resource_generator.rb +1 -1
  99. data/lib/para.rb +25 -21
  100. data/lib/para/attribute_field.rb +2 -0
  101. data/lib/para/attribute_field/base.rb +1 -1
  102. data/lib/para/attribute_field/belongs_to.rb +1 -1
  103. data/lib/para/attribute_field/boolean.rb +1 -1
  104. data/lib/para/attribute_field/friendly_id.rb +19 -0
  105. data/lib/para/attribute_field/has_many.rb +43 -4
  106. data/lib/para/attribute_field/nested_field.rb +46 -0
  107. data/lib/para/attribute_field/nested_many.rb +30 -9
  108. data/lib/para/attribute_field/nested_one.rb +28 -9
  109. data/lib/para/attribute_field/password.rb +1 -1
  110. data/lib/para/attribute_field/relation.rb +19 -1
  111. data/lib/para/attribute_field_mappings.rb +9 -3
  112. data/lib/para/breadcrumbs/breadcrumb.rb +57 -10
  113. data/lib/para/breadcrumbs/controller.rb +19 -6
  114. data/lib/para/breadcrumbs/manager.rb +9 -1
  115. data/lib/para/config.rb +3 -0
  116. data/lib/para/controller_resource.rb +34 -0
  117. data/lib/para/engine.rb +33 -14
  118. data/lib/para/exporter/base.rb +1 -1
  119. data/lib/para/ext.rb +1 -1
  120. data/lib/para/ext/active_record_nested_attributes.rb +51 -0
  121. data/lib/para/ext/paperclip.rb +2 -6
  122. data/lib/para/form_builder.rb +3 -0
  123. data/lib/para/form_builder/attributes_mappings_tracker.rb +45 -0
  124. data/lib/para/form_builder/containers.rb +13 -17
  125. data/lib/para/form_builder/nested_form.rb +2 -3
  126. data/lib/para/helpers.rb +8 -0
  127. data/lib/para/helpers/attributes_mappings.rb +13 -0
  128. data/lib/para/helpers/resource_name.rb +14 -0
  129. data/lib/para/inputs.rb +1 -0
  130. data/lib/para/inputs/multi_select_input.rb +90 -0
  131. data/lib/para/inputs/nested_many_input.rb +6 -1
  132. data/lib/para/markup/resources_table.rb +15 -11
  133. data/lib/para/model_field_parsers.rb +3 -2
  134. data/lib/para/model_field_parsers/base.rb +7 -2
  135. data/lib/para/model_field_parsers/friendly_id.rb +19 -0
  136. data/lib/para/model_field_parsers/relations.rb +20 -1
  137. data/lib/para/page/model.rb +3 -1
  138. data/lib/para/routes.rb +1 -0
  139. data/lib/para/simple_form_config.rb +85 -0
  140. data/lib/para/version.rb +1 -1
  141. data/lib/rails/routing_mapper.rb +5 -8
  142. data/vendor/assets/javascripts/jasny-bootstrap.js +577 -0
  143. data/vendor/assets/stylesheets/animate.css +3340 -0
  144. data/vendor/assets/stylesheets/hint.css +649 -0
  145. data/vendor/assets/stylesheets/jasny-bootstrap.css +287 -0
  146. metadata +114 -67
  147. data/app/assets/images/para/admin/bg.png +0 -0
  148. data/app/assets/javascripts/para/admin.js +0 -20
  149. data/app/assets/stylesheets/admin/theme.sass +0 -0
  150. data/app/assets/stylesheets/para/admin/src/jasny.bootstrap.sass +0 -40
  151. data/app/assets/stylesheets/para/admin/src/nested_many.sass +0 -5
  152. data/app/assets/stylesheets/para/admin/src/page-loading.sass +0 -24
  153. data/app/assets/stylesheets/para/admin/src/page-top-bar.sass +0 -9
  154. data/app/assets/stylesheets/para/admin/src/selectize.sass +0 -46
  155. data/app/assets/stylesheets/para/admin/src/slider.sass +0 -12
  156. data/app/assets/stylesheets/para/admin/theme.sass +0 -22
  157. data/app/assets/stylesheets/para/admin/theme/_base.sass +0 -176
  158. data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +0 -75
  159. data/app/assets/stylesheets/para/admin/theme/_buttons.sass +0 -27
  160. data/app/assets/stylesheets/para/admin/theme/_checkable.sass +0 -108
  161. data/app/assets/stylesheets/para/admin/theme/_commonds.sass +0 -210
  162. data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +0 -52
  163. data/app/assets/stylesheets/para/admin/theme/_form.sass +0 -150
  164. data/app/assets/stylesheets/para/admin/theme/_list.sass +0 -139
  165. data/app/assets/stylesheets/para/admin/theme/_navigation.sass +0 -229
  166. data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +0 -151
  167. data/app/assets/stylesheets/para/admin/theme/_orderable.sass +0 -45
  168. data/app/assets/stylesheets/para/admin/theme/_panel.sass +0 -264
  169. data/app/assets/stylesheets/para/admin/theme/_responsive.sass +0 -123
  170. data/app/assets/stylesheets/para/admin/theme/_table.sass +0 -28
  171. data/app/assets/stylesheets/para/admin/theme/_tree.sass +0 -74
  172. data/app/assets/stylesheets/para/admin/theme/_variables.sass +0 -31
  173. data/app/assets/stylesheets/para/application.css +0 -15
  174. data/app/assets/stylesheets/para/lib/_variables.scss +0 -646
  175. data/app/assets/stylesheets/para/overrides/responsive.sass +0 -62
  176. data/app/assets/stylesheets/para/overrides/theme.sass +0 -172
  177. data/lib/para/ext/cancan.rb +0 -26
  178. data/vendor/assets/javascripts/jasny.bootstrap.min.js +0 -7
  179. data/vendor/assets/stylesheets/animate.min.css +0 -6
  180. data/vendor/assets/stylesheets/jasny.bootstrap.min.css +0 -7
@@ -27,7 +27,8 @@ module Para
27
27
  resources: resources,
28
28
  nested_locals: locals,
29
29
  subclass: subclass,
30
- subclasses: subclasses
30
+ subclasses: subclasses,
31
+ inset: inset?
31
32
  }
32
33
  )
33
34
  end
@@ -59,6 +60,10 @@ module Para
59
60
  (model.try(:descendants) || []).sort_by { |m| m.model_name.human }
60
61
  )
61
62
  end
63
+
64
+ def inset?
65
+ options.fetch(:inset, false)
66
+ end
62
67
  end
63
68
  end
64
69
  end
@@ -44,7 +44,7 @@ module Para
44
44
  # Append cells
45
45
  cells << capture { block.call }
46
46
  # Append actions empty cell
47
- cells << content_tag(:th, '', class: 'actions', width: 1) if actions
47
+ cells << content_tag(:th, '', class: 'table-row-actions') if actions
48
48
 
49
49
  # Output full header
50
50
  content_tag(:thead) do
@@ -131,12 +131,10 @@ module Para
131
131
  end
132
132
 
133
133
  def actions_cell(resource)
134
- content_tag(:td) do
135
- content_tag(:div, class: 'pull-right btn-group') do
136
- actions.map do |type|
137
- send(:"#{ type }_button", resource)
138
- end.compact.join.html_safe
139
- end
134
+ content_tag(:td, class: 'table-row-actions') do
135
+ actions.map do |type|
136
+ send(:"#{ type }_button", resource)
137
+ end.compact.join.html_safe
140
138
  end
141
139
  end
142
140
 
@@ -149,7 +147,10 @@ module Para
149
147
 
150
148
  options = {
151
149
  method: :post,
152
- class: 'btn btn-info'
150
+ class: 'btn btn-sm btn-icon-info btn-shadow hint--left',
151
+ aria: {
152
+ label: ::I18n.t('para.shared.copy')
153
+ }
153
154
  }
154
155
 
155
156
  view.link_to(path, options) do
@@ -162,7 +163,7 @@ module Para
162
163
  resource, action: :edit, return_to: view.request.fullpath
163
164
  )
164
165
 
165
- view.link_to(path, class: 'btn btn-primary') do
166
+ view.link_to(path, class: 'btn btn-sm btn-icon-primary btn-shadow hint--left', aria: { label: ::I18n.t('para.shared.edit') }) do
166
167
  content_tag(:i, '', class: 'fa fa-pencil')
167
168
  end
168
169
  end
@@ -175,11 +176,14 @@ module Para
175
176
  data: {
176
177
  confirm: ::I18n.t('para.list.delete_confirmation')
177
178
  },
178
- class: 'btn btn-danger'
179
+ class: 'btn btn-sm btn-icon-danger btn-shadow hint--left',
180
+ aria: {
181
+ label: ::I18n.t('para.shared.destroy')
182
+ }
179
183
  }
180
184
 
181
185
  view.link_to(path, options) do
182
- content_tag(:i, '', class: 'fa fa-trash')
186
+ content_tag(:i, '', class: 'fa fa-times')
183
187
  end
184
188
  end
185
189
 
@@ -4,9 +4,9 @@ module Para
4
4
  @registered_parsers ||= {}
5
5
  end
6
6
 
7
- def self.parse!(model, fields_hash)
7
+ def self.parse!(model, fields_hash, mappings)
8
8
  registered_parsers.each do |_, parser_class|
9
- parser = parser_class.new(model, fields_hash)
9
+ parser = parser_class.new(model, fields_hash, mappings)
10
10
  parser.parse! if parser.applicable?
11
11
  end
12
12
  end
@@ -20,6 +20,7 @@ require 'para/model_field_parsers/orderable'
20
20
  require 'para/model_field_parsers/relations'
21
21
  require 'para/model_field_parsers/redactor'
22
22
  require 'para/model_field_parsers/globalize'
23
+ require 'para/model_field_parsers/friendly_id'
23
24
  require 'para/model_field_parsers/store'
24
25
  require 'para/model_field_parsers/enums'
25
26
  require 'para/model_field_parsers/closure_tree'
@@ -5,16 +5,21 @@ module Para
5
5
  ModelFieldParsers.registered_parsers[key] = parser
6
6
  end
7
7
 
8
- attr_reader :model, :fields_hash
8
+ attr_reader :model, :fields_hash, :mappings
9
9
 
10
- def initialize(model, fields_hash)
10
+ def initialize(model, fields_hash, mappings)
11
11
  @model = model
12
12
  @fields_hash = fields_hash
13
+ @mappings = mappings
13
14
  end
14
15
 
15
16
  def applicable?
16
17
  true
17
18
  end
19
+
20
+ def find_attributes_for_mapping(type)
21
+ mappings.select { |k, v| v == type.to_s }.keys.map(&:to_sym)
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -0,0 +1,19 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class FriendlyId < Para::ModelFieldParsers::Base
4
+ register :friendly_id, self
5
+
6
+ def parse!
7
+ key = model.friendly_id_config.slug_column
8
+
9
+ fields_hash[key] = AttributeField::FriendlyId.new(
10
+ model, name: key, type: 'friendly_id'
11
+ )
12
+ end
13
+
14
+ def applicable?
15
+ !!model.try(:friendly_id_config)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,6 +4,20 @@ module Para
4
4
  register :relations, self
5
5
 
6
6
  def parse!
7
+ # Catch multi select inputs from form attributes mappings
8
+ find_attributes_for_mapping(:multi_select).each do |attribute|
9
+ fields_hash[attribute] = AttributeField::HasManyField.new(
10
+ model, name: attribute, type: 'has_many', field_type: 'multi_select'
11
+ )
12
+ end
13
+
14
+ # Catch multi select inputs from form attributes mappings
15
+ find_attributes_for_mapping(:nested_many).each do |attribute|
16
+ fields_hash[attribute] = AttributeField::NestedManyField.new(
17
+ model, name: attribute, type: 'has_many', field_type: 'nested_many'
18
+ )
19
+ end
20
+
7
21
  model.reflections.each do |name, reflection|
8
22
  # We ensure that name is a symbol and not a string for 4.2+
9
23
  # versions of AR
@@ -11,11 +25,16 @@ module Para
11
25
  # `fields_hash` is already a `HashWithIndifferentAccess`
12
26
  name = name.to_sym
13
27
 
28
+ # Do not process component relations
14
29
  next if name == :component
30
+ # Do not reprocess attributes that were already catched with
31
+ # attributes mappings above
32
+ next if AttributeField::RelationField == fields_hash[name]
15
33
 
16
34
  # Remove foreign key, if existing, from fields
17
35
  fields_hash.delete(reflection.foreign_key.to_s)
18
36
 
37
+ # Do not process polymorphic belongs to for now ...
19
38
  if reflection.options[:polymorphic] == true
20
39
  fields_hash.delete(reflection.foreign_type.to_s)
21
40
  next
@@ -36,7 +55,7 @@ module Para
36
55
  remove_counter_cache_column!(name, reflection)
37
56
 
38
57
  fields_hash[name] = AttributeField::HasManyField.new(
39
- model, name: name, type: 'has_many', field_type: 'selectize'
58
+ model, name: name, type: 'has_many', field_type: 'multi_select'
40
59
  )
41
60
  elsif !reflection.options[:through]
42
61
  fields_hash[name] = AttributeField::BelongsToField.new(
@@ -4,7 +4,9 @@ module Para
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- has_many :sections, -> { ordered }, class_name: '::Para::Page::Section', as: :page
7
+ has_many :sections, -> { ordered }, class_name: '::Para::Page::Section',
8
+ as: :page,
9
+ dependent: :destroy
8
10
  accepts_nested_attributes_for :sections, allow_destroy: true
9
11
  end
10
12
  end
@@ -14,6 +14,7 @@ module Para
14
14
  scope module: :para do
15
15
  namespace :admin do
16
16
  get '/' => 'main#index'
17
+ get '/search' => 'search#index', as: :search
17
18
  end
18
19
 
19
20
  # Components are namespaced into :admin in their respective methods
@@ -0,0 +1,85 @@
1
+ # Default simple form bootstrap initializer mechanism
2
+ module Para
3
+ module SimpleFormConfig
4
+ def self.wrapper_mappings
5
+ {
6
+ check_boxes: :para_vertical_radio_and_checkboxes,
7
+ radio_buttons: :para_vertical_radio_and_checkboxes,
8
+ boolean: :para_vertical_radio_and_checkboxes,
9
+ datetime: :para_multi_select,
10
+ date: :para_multi_select,
11
+ time: :para_multi_select,
12
+ file: :para_vertical_file_input
13
+ }
14
+ end
15
+
16
+ def self.configure
17
+ # Use this setup block to configure all options available in SimpleForm.
18
+ SimpleForm.setup do |config|
19
+ config.error_notification_class = 'alert alert-danger'
20
+ config.button_class = 'btn'
21
+ config.boolean_label_class = nil
22
+
23
+ config.wrappers :para_vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
24
+ b.use :html5
25
+ b.use :placeholder
26
+ b.optional :maxlength
27
+ b.optional :minlength
28
+ b.optional :pattern
29
+ b.optional :min_max
30
+ b.optional :readonly
31
+ b.use :label, class: 'control-label'
32
+
33
+ b.use :input, class: 'form-control'
34
+ b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
35
+ b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
36
+ end
37
+
38
+ config.wrappers :para_vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
39
+ b.use :html5
40
+ b.use :placeholder
41
+ b.optional :maxlength
42
+ b.optional :minlength
43
+ b.optional :readonly
44
+ b.use :label, class: 'control-label'
45
+
46
+ b.use :input
47
+ b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
48
+ b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
49
+ end
50
+
51
+ config.wrappers :para_vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
52
+ b.use :html5
53
+ b.optional :readonly
54
+
55
+ b.wrapper tag: 'div', class: 'checkbox' do |ba|
56
+ ba.use :label_input
57
+ end
58
+
59
+ b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
60
+ b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
61
+ end
62
+
63
+ config.wrappers :para_vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
64
+ b.use :html5
65
+ b.optional :readonly
66
+ b.use :label, class: 'control-label'
67
+ b.use :input
68
+ b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
69
+ b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
70
+ end
71
+
72
+ config.wrappers :para_multi_select, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
73
+ b.use :html5
74
+ b.optional :readonly
75
+ b.use :label, class: 'control-label'
76
+ b.wrapper tag: 'div', class: 'form-inline' do |ba|
77
+ ba.use :input, class: 'form-control'
78
+ ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
79
+ ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module Para
2
- VERSION = '0.6.9'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -15,16 +15,14 @@ module ActionDispatch
15
15
  end
16
16
 
17
17
  def component(component_name, options = {}, &block)
18
- as, controller, component, endpoint = component_informations_from(
18
+ as, component, endpoint = component_informations_from(
19
19
  component_name, options
20
20
  )
21
21
 
22
- actions = options.fetch(:actions, [:show])
23
-
22
+ controller = options.fetch(:controller, "#{ component_name }_component")
24
23
  imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
25
24
  exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
26
25
 
27
-
28
26
  constraints Para::Routing::ComponentNameConstraint.new(component) do
29
27
  namespace :admin do
30
28
  defaults(component: component) do
@@ -54,7 +52,7 @@ module ActionDispatch
54
52
  options[:scope] ||= ':model'
55
53
  end
56
54
 
57
- as, controller, component, endpoint = component_informations_from(
55
+ as, component, endpoint = component_informations_from(
58
56
  component_name, options
59
57
  )
60
58
 
@@ -105,7 +103,7 @@ module ActionDispatch
105
103
  options[:scope] ||= ':model'
106
104
  end
107
105
 
108
- as, _, component, endpoint = component_informations_from(
106
+ as, component, endpoint = component_informations_from(
109
107
  component_name, options
110
108
  )
111
109
 
@@ -151,10 +149,9 @@ module ActionDispatch
151
149
  def component_informations_from(component_name, options)
152
150
  component = options.fetch(:component, component_name.to_s)
153
151
  as = options.fetch(:as, component_name)
154
- controller = options.fetch(:controller, "#{ component_name }_component")
155
152
  endpoint = ":component/:component_id"
156
153
 
157
- [as, controller, component, endpoint]
154
+ [as, component, endpoint]
158
155
  end
159
156
 
160
157
  def add_extensions_for(type)
@@ -0,0 +1,577 @@
1
+ /*!
2
+ * Jasny Bootstrap v3.1.3 (http://jasny.github.io/bootstrap)
3
+ * Copyright 2012-2014 Arnold Daniels
4
+ * Licensed under Apache-2.0 (https://github.com/jasny/bootstrap/blob/master/LICENSE)
5
+ */
6
+
7
+ if (typeof jQuery === 'undefined') { throw new Error('Jasny Bootstrap\'s JavaScript requires jQuery') }
8
+
9
+ /* ========================================================================
10
+ * Bootstrap: transition.js v3.1.3
11
+ * http://getbootstrap.com/javascript/#transitions
12
+ * ========================================================================
13
+ * Copyright 2011-2014 Twitter, Inc.
14
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
15
+ * ======================================================================== */
16
+
17
+
18
+ +function ($) {
19
+ 'use strict';
20
+
21
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
22
+ // ============================================================
23
+
24
+ function transitionEnd() {
25
+ var el = document.createElement('bootstrap')
26
+
27
+ var transEndEventNames = {
28
+ WebkitTransition : 'webkitTransitionEnd',
29
+ MozTransition : 'transitionend',
30
+ OTransition : 'oTransitionEnd otransitionend',
31
+ transition : 'transitionend'
32
+ }
33
+
34
+ for (var name in transEndEventNames) {
35
+ if (el.style[name] !== undefined) {
36
+ return { end: transEndEventNames[name] }
37
+ }
38
+ }
39
+
40
+ return false // explicit for ie8 ( ._.)
41
+ }
42
+
43
+ if ($.support.transition !== undefined) return // Prevent conflict with Twitter Bootstrap
44
+
45
+ // http://blog.alexmaccaw.com/css-transitions
46
+ $.fn.emulateTransitionEnd = function (duration) {
47
+ var called = false, $el = this
48
+ $(this).one($.support.transition.end, function () { called = true })
49
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
50
+ setTimeout(callback, duration)
51
+ return this
52
+ }
53
+
54
+ $(function () {
55
+ $.support.transition = transitionEnd()
56
+ })
57
+
58
+ }(window.jQuery);
59
+
60
+ /* ========================================================================
61
+ * Bootstrap: offcanvas.js v3.1.3
62
+ * http://jasny.github.io/bootstrap/javascript/#offcanvas
63
+ * ========================================================================
64
+ * Copyright 2013-2014 Arnold Daniels
65
+ *
66
+ * Licensed under the Apache License, Version 2.0 (the "License")
67
+ * you may not use this file except in compliance with the License.
68
+ * You may obtain a copy of the License at
69
+ *
70
+ * http://www.apache.org/licenses/LICENSE-2.0
71
+ *
72
+ * Unless required by applicable law or agreed to in writing, software
73
+ * distributed under the License is distributed on an "AS IS" BASIS,
74
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
75
+ * See the License for the specific language governing permissions and
76
+ * limitations under the License.
77
+ * ======================================================================== */
78
+
79
+ +function ($) { "use strict";
80
+
81
+ // OFFCANVAS PUBLIC CLASS DEFINITION
82
+ // =================================
83
+
84
+ var OffCanvas = function (element, options) {
85
+ this.$element = $(element)
86
+ this.options = $.extend({}, OffCanvas.DEFAULTS, options)
87
+ this.state = null
88
+ this.placement = null
89
+
90
+ if (this.options.recalc) {
91
+ this.calcClone()
92
+ $(window).on('resize', $.proxy(this.recalc, this))
93
+ }
94
+
95
+ if (this.options.autohide)
96
+ $(document).on('click', $.proxy(this.autohide, this))
97
+
98
+ if (this.options.toggle) this.toggle()
99
+
100
+ if (this.options.disablescrolling) {
101
+ this.options.disableScrolling = this.options.disablescrolling
102
+ delete this.options.disablescrolling
103
+ }
104
+ }
105
+
106
+ OffCanvas.DEFAULTS = {
107
+ toggle: true,
108
+ placement: 'auto',
109
+ autohide: true,
110
+ recalc: true,
111
+ disableScrolling: true
112
+ }
113
+
114
+ OffCanvas.prototype.offset = function () {
115
+ switch (this.placement) {
116
+ case 'left':
117
+ case 'right': return this.$element.outerWidth()
118
+ case 'top':
119
+ case 'bottom': return this.$element.outerHeight()
120
+ }
121
+ }
122
+
123
+ OffCanvas.prototype.calcPlacement = function () {
124
+ if (this.options.placement !== 'auto') {
125
+ this.placement = this.options.placement
126
+ return
127
+ }
128
+
129
+ if (!this.$element.hasClass('in')) {
130
+ this.$element.css('visiblity', 'hidden !important').addClass('in')
131
+ }
132
+
133
+ var horizontal = $(window).width() / this.$element.width()
134
+ var vertical = $(window).height() / this.$element.height()
135
+
136
+ var element = this.$element
137
+ function ab(a, b) {
138
+ if (element.css(b) === 'auto') return a
139
+ if (element.css(a) === 'auto') return b
140
+
141
+ var size_a = parseInt(element.css(a), 10)
142
+ var size_b = parseInt(element.css(b), 10)
143
+
144
+ return size_a > size_b ? b : a
145
+ }
146
+
147
+ this.placement = horizontal >= vertical ? ab('left', 'right') : ab('top', 'bottom')
148
+
149
+ if (this.$element.css('visibility') === 'hidden !important') {
150
+ this.$element.removeClass('in').css('visiblity', '')
151
+ }
152
+ }
153
+
154
+ OffCanvas.prototype.opposite = function (placement) {
155
+ switch (placement) {
156
+ case 'top': return 'bottom'
157
+ case 'left': return 'right'
158
+ case 'bottom': return 'top'
159
+ case 'right': return 'left'
160
+ }
161
+ }
162
+
163
+ OffCanvas.prototype.getCanvasElements = function() {
164
+ // Return a set containing the canvas plus all fixed elements
165
+ var canvas = this.options.canvas ? $(this.options.canvas) : this.$element
166
+
167
+ var fixed_elements = canvas.find('*').filter(function() {
168
+ return $(this).css('position') === 'fixed'
169
+ }).not(this.options.exclude)
170
+
171
+ return canvas.add(fixed_elements)
172
+ }
173
+
174
+ OffCanvas.prototype.slide = function (elements, offset, callback) {
175
+ // Use jQuery animation if CSS transitions aren't supported
176
+ if (!$.support.transition) {
177
+ var anim = {}
178
+ anim[this.placement] = "+=" + offset
179
+ return elements.animate(anim, 350, callback)
180
+ }
181
+
182
+ var placement = this.placement
183
+ var opposite = this.opposite(placement)
184
+
185
+ elements.each(function() {
186
+ if ($(this).css(placement) !== 'auto')
187
+ $(this).css(placement, (parseInt($(this).css(placement), 10) || 0) + offset)
188
+
189
+ if ($(this).css(opposite) !== 'auto')
190
+ $(this).css(opposite, (parseInt($(this).css(opposite), 10) || 0) - offset)
191
+ })
192
+
193
+ this.$element
194
+ .one($.support.transition.end, callback)
195
+ .emulateTransitionEnd(350)
196
+ }
197
+
198
+ OffCanvas.prototype.disableScrolling = function() {
199
+ var bodyWidth = $('body').width()
200
+ var prop = 'padding-' + this.opposite(this.placement)
201
+
202
+ if ($('body').data('offcanvas-style') === undefined) {
203
+ $('body').data('offcanvas-style', $('body').attr('style') || '')
204
+ }
205
+
206
+ $('body').css('overflow', 'hidden')
207
+
208
+ if ($('body').width() > bodyWidth) {
209
+ var padding = parseInt($('body').css(prop), 10) + $('body').width() - bodyWidth
210
+
211
+ setTimeout(function() {
212
+ $('body').css(prop, padding)
213
+ }, 1)
214
+ }
215
+ }
216
+
217
+ OffCanvas.prototype.show = function () {
218
+ if (this.state) return
219
+
220
+ var startEvent = $.Event('show.bs.offcanvas')
221
+ this.$element.trigger(startEvent)
222
+ if (startEvent.isDefaultPrevented()) return
223
+
224
+ this.state = 'slide-in'
225
+ this.calcPlacement();
226
+
227
+ var elements = this.getCanvasElements()
228
+ var placement = this.placement
229
+ var opposite = this.opposite(placement)
230
+ var offset = this.offset()
231
+
232
+ if (elements.index(this.$element) !== -1) {
233
+ $(this.$element).data('offcanvas-style', $(this.$element).attr('style') || '')
234
+ this.$element.css(placement, -1 * offset)
235
+ this.$element.css(placement); // Workaround: Need to get the CSS property for it to be applied before the next line of code
236
+ }
237
+
238
+ elements.addClass('canvas-sliding').each(function() {
239
+ if ($(this).data('offcanvas-style') === undefined) $(this).data('offcanvas-style', $(this).attr('style') || '')
240
+ if ($(this).css('position') === 'static') $(this).css('position', 'relative')
241
+ if (($(this).css(placement) === 'auto' || $(this).css(placement) === '0px') &&
242
+ ($(this).css(opposite) === 'auto' || $(this).css(opposite) === '0px')) {
243
+ $(this).css(placement, 0)
244
+ }
245
+ })
246
+
247
+ if (this.options.disableScrolling) this.disableScrolling()
248
+
249
+ var complete = function () {
250
+ if (this.state != 'slide-in') return
251
+
252
+ this.state = 'slid'
253
+
254
+ elements.removeClass('canvas-sliding').addClass('canvas-slid')
255
+ this.$element.trigger('shown.bs.offcanvas')
256
+ }
257
+
258
+ setTimeout($.proxy(function() {
259
+ this.$element.addClass('in')
260
+ this.slide(elements, offset, $.proxy(complete, this))
261
+ }, this), 1)
262
+ }
263
+
264
+ OffCanvas.prototype.hide = function (fast) {
265
+ if (this.state !== 'slid') return
266
+
267
+ var startEvent = $.Event('hide.bs.offcanvas')
268
+ this.$element.trigger(startEvent)
269
+ if (startEvent.isDefaultPrevented()) return
270
+
271
+ this.state = 'slide-out'
272
+
273
+ var elements = $('.canvas-slid')
274
+ var placement = this.placement
275
+ var offset = -1 * this.offset()
276
+
277
+ var complete = function () {
278
+ if (this.state != 'slide-out') return
279
+
280
+ this.state = null
281
+ this.placement = null
282
+
283
+ this.$element.removeClass('in')
284
+
285
+ elements.removeClass('canvas-sliding')
286
+ elements.add(this.$element).add('body').each(function() {
287
+ $(this).attr('style', $(this).data('offcanvas-style')).removeData('offcanvas-style')
288
+ })
289
+
290
+ this.$element.trigger('hidden.bs.offcanvas')
291
+ }
292
+
293
+ elements.removeClass('canvas-slid').addClass('canvas-sliding')
294
+
295
+ setTimeout($.proxy(function() {
296
+ this.slide(elements, offset, $.proxy(complete, this))
297
+ }, this), 1)
298
+ }
299
+
300
+ OffCanvas.prototype.toggle = function () {
301
+ if (this.state === 'slide-in' || this.state === 'slide-out') return
302
+ this[this.state === 'slid' ? 'hide' : 'show']()
303
+ }
304
+
305
+ OffCanvas.prototype.calcClone = function() {
306
+ this.$calcClone = this.$element.clone()
307
+ .html('')
308
+ .addClass('offcanvas-clone').removeClass('in')
309
+ .appendTo($('body'))
310
+ }
311
+
312
+ OffCanvas.prototype.recalc = function () {
313
+ if (this.$calcClone.css('display') === 'none' || (this.state !== 'slid' && this.state !== 'slide-in')) return
314
+
315
+ this.state = null
316
+ this.placement = null
317
+ var elements = this.getCanvasElements()
318
+
319
+ this.$element.removeClass('in')
320
+
321
+ elements.removeClass('canvas-slid')
322
+ elements.add(this.$element).add('body').each(function() {
323
+ $(this).attr('style', $(this).data('offcanvas-style')).removeData('offcanvas-style')
324
+ })
325
+ }
326
+
327
+ OffCanvas.prototype.autohide = function (e) {
328
+ if ($(e.target).closest(this.$element).length === 0) this.hide()
329
+ }
330
+
331
+ // OFFCANVAS PLUGIN DEFINITION
332
+ // ==========================
333
+
334
+ var old = $.fn.offcanvas
335
+
336
+ $.fn.offcanvas = function (option) {
337
+ return this.each(function () {
338
+ var $this = $(this)
339
+ var data = $this.data('bs.offcanvas')
340
+ var options = $.extend({}, OffCanvas.DEFAULTS, $this.data(), typeof option === 'object' && option)
341
+
342
+ if (!data) $this.data('bs.offcanvas', (data = new OffCanvas(this, options)))
343
+ if (typeof option === 'string') data[option]()
344
+ })
345
+ }
346
+
347
+ $.fn.offcanvas.Constructor = OffCanvas
348
+
349
+
350
+ // OFFCANVAS NO CONFLICT
351
+ // ====================
352
+
353
+ $.fn.offcanvas.noConflict = function () {
354
+ $.fn.offcanvas = old
355
+ return this
356
+ }
357
+
358
+
359
+ // OFFCANVAS DATA-API
360
+ // =================
361
+
362
+ $(document).on('click.bs.offcanvas.data-api', '[data-toggle=offcanvas]', function (e) {
363
+ var $this = $(this), href
364
+ var target = $this.attr('data-target')
365
+ || e.preventDefault()
366
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
367
+ var $canvas = $(target)
368
+ var data = $canvas.data('bs.offcanvas')
369
+ var option = data ? 'toggle' : $this.data()
370
+
371
+ e.stopPropagation()
372
+
373
+ if (data) data.toggle()
374
+ else $canvas.offcanvas(option)
375
+ })
376
+
377
+ }(window.jQuery);
378
+
379
+
380
+ /* ===========================================================
381
+ * Bootstrap: fileinput.js v3.1.3
382
+ * http://jasny.github.com/bootstrap/javascript/#fileinput
383
+ * ===========================================================
384
+ * Copyright 2012-2014 Arnold Daniels
385
+ *
386
+ * Licensed under the Apache License, Version 2.0 (the "License")
387
+ * you may not use this file except in compliance with the License.
388
+ * You may obtain a copy of the License at
389
+ *
390
+ * http://www.apache.org/licenses/LICENSE-2.0
391
+ *
392
+ * Unless required by applicable law or agreed to in writing, software
393
+ * distributed under the License is distributed on an "AS IS" BASIS,
394
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
395
+ * See the License for the specific language governing permissions and
396
+ * limitations under the License.
397
+ * ========================================================== */
398
+
399
+ +function ($) { "use strict";
400
+
401
+ var isIE = window.navigator.appName == 'Microsoft Internet Explorer'
402
+
403
+ // FILEUPLOAD PUBLIC CLASS DEFINITION
404
+ // =================================
405
+
406
+ var Fileinput = function (element, options) {
407
+ this.$element = $(element)
408
+
409
+ this.$input = this.$element.find(':file')
410
+ if (this.$input.length === 0) return
411
+
412
+ this.name = this.$input.attr('name') || options.name
413
+
414
+ this.$hidden = this.$element.find('input[type=hidden][name="' + this.name + '"]')
415
+ if (this.$hidden.length === 0) {
416
+ this.$hidden = $('<input type="hidden">').insertBefore(this.$input)
417
+ }
418
+
419
+ this.$preview = this.$element.find('.fileinput-preview')
420
+ var height = this.$preview.css('height')
421
+ if (this.$preview.css('display') !== 'inline' && height !== '0px' && height !== 'none') {
422
+ this.$preview.css('line-height', height)
423
+ }
424
+
425
+ this.original = {
426
+ exists: this.$element.hasClass('fileinput-exists'),
427
+ preview: this.$preview.html(),
428
+ hiddenVal: this.$hidden.val()
429
+ }
430
+
431
+ this.listen()
432
+ }
433
+
434
+ Fileinput.prototype.listen = function() {
435
+ this.$input.on('change.bs.fileinput', $.proxy(this.change, this))
436
+ $(this.$input[0].form).on('reset.bs.fileinput', $.proxy(this.reset, this))
437
+
438
+ this.$element.find('[data-trigger="fileinput"]').on('click.bs.fileinput', $.proxy(this.trigger, this))
439
+ this.$element.find('[data-dismiss="fileinput"]').on('click.bs.fileinput', $.proxy(this.clear, this))
440
+ },
441
+
442
+ Fileinput.prototype.change = function(e) {
443
+ var files = e.target.files === undefined ? (e.target && e.target.value ? [{ name: e.target.value.replace(/^.+\\/, '')}] : []) : e.target.files
444
+
445
+ e.stopPropagation()
446
+
447
+ if (files.length === 0) {
448
+ this.clear()
449
+ return
450
+ }
451
+
452
+ this.$hidden.val('')
453
+ this.$hidden.attr('name', '')
454
+ this.$input.attr('name', this.name)
455
+
456
+ var file = files[0]
457
+
458
+ if (this.$preview.length > 0 && (typeof file.type !== "undefined" ? file.type.match(/^image\/(gif|png|jpeg)$/) : file.name.match(/\.(gif|png|jpe?g)$/i)) && typeof FileReader !== "undefined") {
459
+ var reader = new FileReader()
460
+ var preview = this.$preview
461
+ var element = this.$element
462
+
463
+ reader.onload = function(re) {
464
+ var $img = $('<img>')
465
+ $img[0].src = re.target.result
466
+ files[0].result = re.target.result
467
+
468
+ element.find('.fileinput-filename').text(file.name)
469
+
470
+ // if parent has max-height, using `(max-)height: 100%` on child doesn't take padding and border into account
471
+ if (preview.css('max-height') != 'none') $img.css('max-height', parseInt(preview.css('max-height'), 10) - parseInt(preview.css('padding-top'), 10) - parseInt(preview.css('padding-bottom'), 10) - parseInt(preview.css('border-top'), 10) - parseInt(preview.css('border-bottom'), 10))
472
+
473
+ preview.html($img)
474
+ element.addClass('fileinput-exists').removeClass('fileinput-new')
475
+
476
+ element.trigger('change.bs.fileinput', files)
477
+ }
478
+
479
+ reader.readAsDataURL(file)
480
+ } else {
481
+ this.$element.find('.fileinput-filename').text(file.name)
482
+ this.$preview.text(file.name)
483
+
484
+ this.$element.addClass('fileinput-exists').removeClass('fileinput-new')
485
+
486
+ this.$element.trigger('change.bs.fileinput')
487
+ }
488
+ },
489
+
490
+ Fileinput.prototype.clear = function(e) {
491
+ if (e) e.preventDefault()
492
+
493
+ this.$hidden.val('')
494
+ this.$hidden.attr('name', this.name)
495
+ this.$input.attr('name', '')
496
+
497
+ //ie8+ doesn't support changing the value of input with type=file so clone instead
498
+ if (isIE) {
499
+ var inputClone = this.$input.clone(true);
500
+ this.$input.after(inputClone);
501
+ this.$input.remove();
502
+ this.$input = inputClone;
503
+ } else {
504
+ this.$input.val('')
505
+ }
506
+
507
+ this.$preview.html('')
508
+ this.$element.find('.fileinput-filename').text('')
509
+ this.$element.addClass('fileinput-new').removeClass('fileinput-exists')
510
+
511
+ if (e !== undefined) {
512
+ this.$input.trigger('change')
513
+ this.$element.trigger('clear.bs.fileinput')
514
+ }
515
+ },
516
+
517
+ Fileinput.prototype.reset = function() {
518
+ this.clear()
519
+
520
+ this.$hidden.val(this.original.hiddenVal)
521
+ this.$preview.html(this.original.preview)
522
+ this.$element.find('.fileinput-filename').text('')
523
+
524
+ if (this.original.exists) this.$element.addClass('fileinput-exists').removeClass('fileinput-new')
525
+ else this.$element.addClass('fileinput-new').removeClass('fileinput-exists')
526
+
527
+ this.$element.trigger('reset.bs.fileinput')
528
+ },
529
+
530
+ Fileinput.prototype.trigger = function(e) {
531
+ this.$input.trigger('click')
532
+ e.preventDefault()
533
+ }
534
+
535
+
536
+ // FILEUPLOAD PLUGIN DEFINITION
537
+ // ===========================
538
+
539
+ var old = $.fn.fileinput
540
+
541
+ $.fn.fileinput = function (options) {
542
+ return this.each(function () {
543
+ var $this = $(this),
544
+ data = $this.data('bs.fileinput')
545
+ if (!data) $this.data('bs.fileinput', (data = new Fileinput(this, options)))
546
+ if (typeof options == 'string') data[options]()
547
+ })
548
+ }
549
+
550
+ $.fn.fileinput.Constructor = Fileinput
551
+
552
+
553
+ // FILEINPUT NO CONFLICT
554
+ // ====================
555
+
556
+ $.fn.fileinput.noConflict = function () {
557
+ $.fn.fileinput = old
558
+ return this
559
+ }
560
+
561
+
562
+ // FILEUPLOAD DATA-API
563
+ // ==================
564
+
565
+ $(document).on('click.fileinput.data-api', '[data-provides="fileinput"]', function (e) {
566
+ var $this = $(this)
567
+ if ($this.data('bs.fileinput')) return
568
+ $this.fileinput($this.data())
569
+
570
+ var $target = $(e.target).closest('[data-dismiss="fileinput"],[data-trigger="fileinput"]');
571
+ if ($target.length > 0) {
572
+ e.preventDefault()
573
+ $target.trigger('click.bs.fileinput')
574
+ }
575
+ })
576
+
577
+ }(window.jQuery);