para 0.6.9 → 0.7.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 (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);