para 0.7.1 → 0.7.2
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/para/admin/tabs.coffee +20 -1
- data/app/assets/javascripts/para/admin.coffee +1 -0
- data/app/assets/javascripts/para/inputs/multi-select-input.coffee +5 -3
- data/app/assets/javascripts/para/inputs/nested_many.coffee +35 -1
- data/app/assets/javascripts/para/lib/remote-file-forms.coffee +6 -7
- data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +1 -1
- data/app/assets/stylesheets/para/admin/main.sass +1 -0
- data/app/assets/stylesheets/para/admin/src/_affix.sass +4 -0
- data/app/assets/stylesheets/para/admin/src/_base.sass +4 -1
- data/app/assets/stylesheets/para/admin/src/_breadcrumb.sass +12 -2
- data/app/assets/stylesheets/para/admin/src/_form.sass +22 -3
- data/app/assets/stylesheets/para/admin/src/_navtabs.sass +23 -6
- data/app/assets/stylesheets/para/admin/src/_nested-many.sass +5 -0
- data/app/assets/stylesheets/para/admin/src/_nested_one.sass +6 -5
- data/app/controllers/para/admin/base_controller.rb +5 -0
- data/app/controllers/para/admin/crud_resources_controller.rb +1 -1
- data/app/controllers/para/admin/exports_controller.rb +2 -1
- data/app/controllers/para/admin/nested_forms_controller.rb +13 -0
- data/app/controllers/para/admin/resources_controller.rb +14 -3
- data/app/controllers/para/admin/search_controller.rb +7 -5
- data/app/decorators/para/component/base_decorator.rb +7 -2
- data/app/decorators/para/component/crud_decorator.rb +4 -0
- data/app/decorators/para/component/form_decorator.rb +6 -0
- data/app/exporters/call_for_projects_votes_exporter.rb +79 -0
- data/app/helpers/para/admin/base_helper.rb +2 -3
- data/app/helpers/para/admin/history_helper.rb +16 -0
- data/app/helpers/para/form_helper.rb +9 -1
- data/app/helpers/para/model_helper.rb +0 -2
- data/app/models/para/cache/item.rb +4 -0
- data/app/models/para/component/base.rb +4 -0
- data/app/models/para/component/crud.rb +1 -0
- data/app/models/para/component/form.rb +2 -0
- data/app/models/para/library/file.rb +40 -12
- data/app/models/para/page/section.rb +13 -1
- data/app/views/admin/para/exporter/bases/_completed.html.haml +1 -1
- data/app/views/layouts/para/admin.html.haml +5 -5
- data/app/views/para/admin/form_resources/show.html.haml +6 -2
- data/app/views/para/admin/nested_forms/show.html.haml +9 -0
- data/app/views/para/admin/resources/_exports_menu.html.haml +2 -2
- data/app/views/para/admin/resources/_list.html.haml +26 -21
- data/app/views/para/admin/resources/_remote_nested_form.html.haml +1 -0
- data/app/views/para/admin/resources/edit.html.haml +7 -2
- data/app/views/para/admin/resources/history/_index.html.haml +3 -0
- data/app/views/para/admin/shared/_header.html.haml +3 -3
- data/app/views/para/admin/shared/_navigation.html.haml +1 -1
- data/app/views/para/form/_tabs.html.haml +12 -9
- data/app/views/para/inputs/_multi_select.html.haml +6 -2
- data/app/views/para/inputs/_nested_many.html.haml +4 -4
- data/app/views/para/inputs/_nested_one.html.haml +12 -1
- data/app/views/para/inputs/multi_select/_no_items.html.haml +8 -4
- data/app/views/para/inputs/nested_many/_add.html.haml +2 -2
- data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +3 -3
- data/app/views/para/inputs/nested_many/_container.html.haml +10 -7
- data/lib/generators/para/exporter/templates/csv_exporter.rb +24 -0
- data/lib/generators/para/exporter/templates/xls_exporter.rb +24 -0
- data/lib/para/attribute_field/belongs_to.rb +4 -1
- data/lib/para/attribute_field/nested_field.rb +4 -2
- data/lib/para/attribute_field/nested_many.rb +1 -1
- data/lib/para/attribute_field/nested_one.rb +1 -1
- data/lib/para/cache/database_store.rb +14 -1
- data/lib/para/cloneable.rb +44 -2
- data/lib/para/component/history.rb +15 -0
- data/lib/para/component.rb +1 -0
- data/lib/para/components_configuration.rb +8 -3
- data/lib/para/engine.rb +8 -0
- data/lib/para/exporter/base.rb +49 -1
- data/lib/para/ext/request_iframe_xhr.rb +17 -0
- data/lib/para/ext.rb +1 -0
- data/lib/para/form_builder/attributes_mappings_tracker.rb +15 -3
- data/lib/para/form_builder/containers.rb +6 -1
- data/lib/para/form_builder/nested_form.rb +7 -4
- data/lib/para/form_builder/tabs.rb +49 -8
- data/lib/para/iframe_transport/middleware.rb +58 -0
- data/lib/para/iframe_transport.rb +7 -0
- data/lib/para/importer/base.rb +1 -0
- data/lib/para/inputs/multi_select_input.rb +27 -8
- data/lib/para/inputs/nested_base_input.rb +26 -0
- data/lib/para/inputs/nested_many_input.rb +43 -18
- data/lib/para/inputs/nested_one_input.rb +18 -4
- data/lib/para/inputs.rb +1 -0
- data/lib/para/log_config.rb +14 -0
- data/lib/para/markup/resources_table.rb +2 -2
- data/lib/para/model_field_parsers/relations.rb +12 -1
- data/lib/para/page/model.rb +2 -1
- data/lib/para/postgres_extensions_checker.rb +15 -11
- data/lib/para/routes.rb +3 -3
- data/lib/para/search/distinct.rb +1 -1
- data/lib/para/version.rb +1 -1
- data/lib/para.rb +2 -0
- data/lib/rails/routing_mapper.rb +19 -27
- data/vendor/assets/javascripts/jquery.iframe-transport.js +260 -0
- data/vendor/assets/javascripts/jquery.remote-modal-form.coffee +80 -2
- metadata +23 -9
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
module Para
|
|
2
2
|
module Inputs
|
|
3
|
-
class NestedOneInput <
|
|
3
|
+
class NestedOneInput < NestedBaseInput
|
|
4
4
|
def input(wrapper_options = nil)
|
|
5
5
|
input_html_options[:class] << "nested-one"
|
|
6
6
|
|
|
7
7
|
parent_model = object.class
|
|
8
8
|
association = object.association(attribute_name)
|
|
9
9
|
relation = parent_model.reflect_on_association(attribute_name)
|
|
10
|
-
model = relation.klass
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
resource = object.send(attribute_name)
|
|
12
|
+
model = (resource && resource.class) || relation.klass
|
|
13
|
+
|
|
14
|
+
unless resource
|
|
13
15
|
# Build association without trying to save the new record
|
|
14
16
|
resource = case association
|
|
17
|
+
when ActiveRecord::Associations::HasOneThroughAssociation
|
|
18
|
+
association.replace(model.new)
|
|
15
19
|
when ActiveRecord::Associations::HasOneAssociation
|
|
16
20
|
association.replace(model.new, false)
|
|
17
21
|
else
|
|
@@ -19,16 +23,26 @@ module Para
|
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
locals = options.fetch(:locals, {})
|
|
27
|
+
|
|
22
28
|
template.render(
|
|
23
29
|
partial: 'para/inputs/nested_one',
|
|
24
30
|
locals: {
|
|
25
31
|
form: @builder,
|
|
26
32
|
model: model,
|
|
27
33
|
resource: resource,
|
|
28
|
-
attribute_name: attribute_name
|
|
34
|
+
attribute_name: attribute_name,
|
|
35
|
+
nested_locals: locals,
|
|
36
|
+
collapsible: collapsible
|
|
29
37
|
}
|
|
30
38
|
)
|
|
31
39
|
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def collapsible
|
|
44
|
+
options.fetch(:collapsible, false)
|
|
45
|
+
end
|
|
32
46
|
end
|
|
33
47
|
end
|
|
34
48
|
end
|
data/lib/para/inputs.rb
CHANGED
|
@@ -169,7 +169,7 @@ module Para
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
def delete_button(resource)
|
|
172
|
-
path = component.relation_path(resource)
|
|
172
|
+
path = component.relation_path(resource, return_to: view.request.fullpath)
|
|
173
173
|
|
|
174
174
|
options = {
|
|
175
175
|
method: :delete,
|
|
@@ -178,7 +178,7 @@ module Para
|
|
|
178
178
|
},
|
|
179
179
|
class: 'btn btn-sm btn-icon-danger btn-shadow hint--left',
|
|
180
180
|
aria: {
|
|
181
|
-
label: ::I18n.t('para.shared.destroy')
|
|
181
|
+
label: ::I18n.t('para.shared.destroy')
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -11,13 +11,24 @@ module Para
|
|
|
11
11
|
)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
# Catch multi select inputs from form attributes mappings
|
|
15
14
|
find_attributes_for_mapping(:nested_many).each do |attribute|
|
|
16
15
|
fields_hash[attribute] = AttributeField::NestedManyField.new(
|
|
17
16
|
model, name: attribute, type: 'has_many', field_type: 'nested_many'
|
|
18
17
|
)
|
|
19
18
|
end
|
|
20
19
|
|
|
20
|
+
find_attributes_for_mapping(:nested_one).each do |attribute|
|
|
21
|
+
fields_hash[attribute] = AttributeField::NestedOneField.new(
|
|
22
|
+
model, name: attribute, type: 'has_one', field_type: 'nested_one'
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
find_attributes_for_mapping(:selectize).each do |attribute|
|
|
27
|
+
fields_hash[attribute] = AttributeField::BelongsToField.new(
|
|
28
|
+
model, name: attribute, type: 'belongs_to', field_type: 'selectize'
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
21
32
|
model.reflections.each do |name, reflection|
|
|
22
33
|
# We ensure that name is a symbol and not a string for 4.2+
|
|
23
34
|
# versions of AR
|
data/lib/para/page/model.rb
CHANGED
|
@@ -2,15 +2,17 @@ module Para
|
|
|
2
2
|
class PostgresExtensionsChecker
|
|
3
3
|
class << self
|
|
4
4
|
def check_all
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
Para::LogConfig.with_log_level(:fatal) do
|
|
6
|
+
%w(hstore unaccent).each do |extname|
|
|
7
|
+
unless extension_exists?(extname)
|
|
8
|
+
# Could not use Rails.logger here, using puts as a temporary
|
|
9
|
+
# solution.
|
|
10
|
+
puts "[Warning] PostgreSQL \"#{ extname }\" extension is not " +
|
|
11
|
+
"installed in your database. This means that you " +
|
|
12
|
+
"missing some migrations that you can install " +
|
|
13
|
+
"with the following command : " +
|
|
14
|
+
"`rake para_engine:install:migrations` and then migrate."
|
|
15
|
+
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -18,10 +20,12 @@ module Para
|
|
|
18
20
|
private
|
|
19
21
|
|
|
20
22
|
def extension_exists?(extname)
|
|
21
|
-
ActiveRecord::Base.connection.execute(
|
|
23
|
+
result = ActiveRecord::Base.connection.execute(
|
|
22
24
|
"SELECT COUNT(*) FROM pg_catalog.pg_extension " +
|
|
23
25
|
"WHERE extname = '#{ extname }'"
|
|
24
|
-
).first
|
|
26
|
+
).first
|
|
27
|
+
|
|
28
|
+
result && result['count'].to_i > 0
|
|
25
29
|
rescue ActiveRecord::NoDatabaseError
|
|
26
30
|
true # Do not issue warning when no database is installed
|
|
27
31
|
end
|
data/lib/para/routes.rb
CHANGED
|
@@ -18,9 +18,9 @@ module Para
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Components are namespaced into :admin in their respective methods
|
|
21
|
-
crud_component
|
|
22
|
-
form_component
|
|
23
|
-
component :settings
|
|
21
|
+
crud_component scoped_in_para: true
|
|
22
|
+
form_component scoped_in_para: true
|
|
23
|
+
component :settings, scoped_in_para: true
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
block.call if block
|
data/lib/para/search/distinct.rb
CHANGED
data/lib/para/version.rb
CHANGED
data/lib/para.rb
CHANGED
|
@@ -33,6 +33,7 @@ require 'vertebra'
|
|
|
33
33
|
require 'rails/routing_mapper'
|
|
34
34
|
require 'rails/relation_length_validator'
|
|
35
35
|
|
|
36
|
+
require 'para/log_config'
|
|
36
37
|
require 'para/postgres_extensions_checker'
|
|
37
38
|
|
|
38
39
|
module Para
|
|
@@ -50,6 +51,7 @@ module Para
|
|
|
50
51
|
autoload :Breadcrumbs
|
|
51
52
|
autoload :Cache
|
|
52
53
|
autoload :Logging
|
|
54
|
+
autoload :IframeTransport
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
def self.config(&block)
|
data/lib/rails/routing_mapper.rb
CHANGED
|
@@ -20,8 +20,6 @@ module ActionDispatch
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
controller = options.fetch(:controller, "#{ component_name }_component")
|
|
23
|
-
imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
|
|
24
|
-
exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
|
|
25
23
|
|
|
26
24
|
constraints Para::Routing::ComponentNameConstraint.new(component) do
|
|
27
25
|
namespace :admin do
|
|
@@ -33,13 +31,7 @@ module ActionDispatch
|
|
|
33
31
|
instance_eval(&block) if block
|
|
34
32
|
add_extensions_for(:component)
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
resources :imports, controller: imports_controller
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
scope ':exporter' do
|
|
41
|
-
resources :exports, controller: exports_controller
|
|
42
|
-
end
|
|
34
|
+
common_component_routes(options)
|
|
43
35
|
end
|
|
44
36
|
end
|
|
45
37
|
end
|
|
@@ -60,8 +52,6 @@ module ActionDispatch
|
|
|
60
52
|
# namespacing issues in plugins and other module namespaced scenarios
|
|
61
53
|
#
|
|
62
54
|
controller = options.fetch(:controller, '/para/admin/crud_resources')
|
|
63
|
-
imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
|
|
64
|
-
exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
|
|
65
55
|
|
|
66
56
|
constraints Para::Routing::ComponentNameConstraint.new(component) do
|
|
67
57
|
constraints Para::Routing::ComponentControllerConstraint.new(controller) do
|
|
@@ -82,13 +72,7 @@ module ActionDispatch
|
|
|
82
72
|
add_extensions_for(:crud_component)
|
|
83
73
|
end
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
resources :imports, controller: imports_controller
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
scope ':exporter' do
|
|
90
|
-
resources :exports, controller: exports_controller
|
|
91
|
-
end
|
|
75
|
+
common_component_routes(options)
|
|
92
76
|
end
|
|
93
77
|
end
|
|
94
78
|
end
|
|
@@ -108,8 +92,6 @@ module ActionDispatch
|
|
|
108
92
|
)
|
|
109
93
|
|
|
110
94
|
controller = options.fetch(:controller, '/para/admin/form_resources')
|
|
111
|
-
imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
|
|
112
|
-
exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
|
|
113
95
|
|
|
114
96
|
constraints Para::Routing::ComponentNameConstraint.new(component) do
|
|
115
97
|
constraints Para::Routing::ComponentControllerConstraint.new(controller) do
|
|
@@ -119,13 +101,7 @@ module ActionDispatch
|
|
|
119
101
|
add_extensions_for(:form_component)
|
|
120
102
|
end
|
|
121
103
|
|
|
122
|
-
|
|
123
|
-
resources :imports, controller: imports_controller
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
scope ':exporter' do
|
|
127
|
-
resources :exports, controller: exports_controller
|
|
128
|
-
end
|
|
104
|
+
common_component_routes(options)
|
|
129
105
|
end
|
|
130
106
|
end
|
|
131
107
|
end
|
|
@@ -159,6 +135,22 @@ module ActionDispatch
|
|
|
159
135
|
instance_eval(&extension)
|
|
160
136
|
end
|
|
161
137
|
end
|
|
138
|
+
|
|
139
|
+
def common_component_routes(options)
|
|
140
|
+
nested_forms_controller = options.fetch(:nested_forms_controller, '/para/admin/nested_forms')
|
|
141
|
+
imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
|
|
142
|
+
exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
|
|
143
|
+
|
|
144
|
+
resource 'nested_form', controller: nested_forms_controller, only: [:show]
|
|
145
|
+
|
|
146
|
+
scope ':importer' do
|
|
147
|
+
resources :imports, controller: imports_controller
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
scope ':exporter' do
|
|
151
|
+
resources :exports, controller: exports_controller
|
|
152
|
+
end
|
|
153
|
+
end
|
|
162
154
|
end
|
|
163
155
|
end
|
|
164
156
|
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// This [jQuery](https://jquery.com/) plugin implements an `<iframe>`
|
|
2
|
+
// [transport](https://api.jquery.com/jQuery.ajax/#extending-ajax) so that
|
|
3
|
+
// `$.ajax()` calls support the uploading of files using standard HTML file
|
|
4
|
+
// input fields. This is done by switching the exchange from `XMLHttpRequest`
|
|
5
|
+
// to a hidden `iframe` element containing a form that is submitted.
|
|
6
|
+
|
|
7
|
+
// The [source for the plugin](https://github.com/cmlenz/jquery-iframe-transport)
|
|
8
|
+
// is available on [Github](https://github.com/) and licensed under the [MIT
|
|
9
|
+
// license](https://github.com/cmlenz/jquery-iframe-transport/blob/master/LICENSE).
|
|
10
|
+
|
|
11
|
+
// ## Usage
|
|
12
|
+
|
|
13
|
+
// To use this plugin, you simply add an `iframe` option with the value `true`
|
|
14
|
+
// to the Ajax settings an `$.ajax()` call, and specify the file fields to
|
|
15
|
+
// include in the submssion using the `files` option, which can be a selector,
|
|
16
|
+
// jQuery object, or a list of DOM elements containing one or more
|
|
17
|
+
// `<input type="file">` elements:
|
|
18
|
+
|
|
19
|
+
// $("#myform").submit(function() {
|
|
20
|
+
// $.ajax(this.action, {
|
|
21
|
+
// files: $(":file", this),
|
|
22
|
+
// iframe: true
|
|
23
|
+
// }).complete(function(data) {
|
|
24
|
+
// });
|
|
25
|
+
// });
|
|
26
|
+
|
|
27
|
+
// The plugin will construct hidden `<iframe>` and `<form>` elements, add the
|
|
28
|
+
// file field(s) to that form, submit the form, and process the response.
|
|
29
|
+
|
|
30
|
+
// If you want to include other form fields in the form submission, include
|
|
31
|
+
// them in the `data` option, and set the `processData` option to `false`:
|
|
32
|
+
|
|
33
|
+
// $("#myform").submit(function() {
|
|
34
|
+
// $.ajax(this.action, {
|
|
35
|
+
// data: $(":text", this).serializeArray(),
|
|
36
|
+
// files: $(":file", this),
|
|
37
|
+
// iframe: true,
|
|
38
|
+
// processData: false
|
|
39
|
+
// }).complete(function(data) {
|
|
40
|
+
// });
|
|
41
|
+
// });
|
|
42
|
+
|
|
43
|
+
// ### Response Data Types
|
|
44
|
+
|
|
45
|
+
// As the transport does not have access to the HTTP headers of the server
|
|
46
|
+
// response, it is not as simple to make use of the automatic content type
|
|
47
|
+
// detection provided by jQuery as with regular XHR. If you can't set the
|
|
48
|
+
// expected response data type (for example because it may vary depending on
|
|
49
|
+
// the outcome of processing by the server), you will need to employ a
|
|
50
|
+
// workaround on the server side: Send back an HTML document containing just a
|
|
51
|
+
// `<textarea>` element with a `data-type` attribute that specifies the MIME
|
|
52
|
+
// type, and put the actual payload in the textarea:
|
|
53
|
+
|
|
54
|
+
// <textarea data-type="application/json">
|
|
55
|
+
// {"ok": true, "message": "Thanks so much"}
|
|
56
|
+
// </textarea>
|
|
57
|
+
|
|
58
|
+
// The iframe transport plugin will detect this and pass the value of the
|
|
59
|
+
// `data-type` attribute on to jQuery as if it was the "Content-Type" response
|
|
60
|
+
// header, thereby enabling the same kind of conversions that jQuery applies
|
|
61
|
+
// to regular responses. For the example above you should get a Javascript
|
|
62
|
+
// object as the `data` parameter of the `complete` callback, with the
|
|
63
|
+
// properties `ok: true` and `message: "Thanks so much"`.
|
|
64
|
+
|
|
65
|
+
// ### Handling Server Errors
|
|
66
|
+
|
|
67
|
+
// Another problem with using an `iframe` for file uploads is that it is
|
|
68
|
+
// impossible for the javascript code to determine the HTTP status code of the
|
|
69
|
+
// servers response. Effectively, all of the calls you make will look like they
|
|
70
|
+
// are getting successful responses, and thus invoke the `done()` or
|
|
71
|
+
// `complete()` callbacks. You can only communicate problems using the content
|
|
72
|
+
// of the response payload. For example, consider using a JSON response such as
|
|
73
|
+
// the following to indicate a problem with an uploaded file:
|
|
74
|
+
|
|
75
|
+
// <textarea data-type="application/json">
|
|
76
|
+
// {"ok": false, "message": "Please only upload reasonably sized files."}
|
|
77
|
+
// </textarea>
|
|
78
|
+
|
|
79
|
+
// ### Compatibility
|
|
80
|
+
|
|
81
|
+
// This plugin has primarily been tested on Safari 5 (or later), Firefox 4 (or
|
|
82
|
+
// later), and Internet Explorer (all the way back to version 6). While I
|
|
83
|
+
// haven't found any issues with it so far, I'm fairly sure it still doesn't
|
|
84
|
+
// work around all the quirks in all different browsers. But the code is still
|
|
85
|
+
// pretty simple overall, so you should be able to fix it and contribute a
|
|
86
|
+
// patch :)
|
|
87
|
+
|
|
88
|
+
// ## Annotated Source
|
|
89
|
+
|
|
90
|
+
(function($, undefined) {
|
|
91
|
+
// Register a prefilter that checks whether the `iframe` option is set, and
|
|
92
|
+
// switches to the "iframe" data type if it is `true`.
|
|
93
|
+
$.ajaxPrefilter(function(options, origOptions, jqXHR) {
|
|
94
|
+
if (options.iframe) {
|
|
95
|
+
options.originalURL = options.url;
|
|
96
|
+
return "iframe";
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Register a transport for the "iframe" data type. It will only activate
|
|
101
|
+
// when the "files" option has been set to a non-empty list of enabled file
|
|
102
|
+
// inputs.
|
|
103
|
+
$.ajaxTransport("iframe", function(options, origOptions, jqXHR) {
|
|
104
|
+
var form = null,
|
|
105
|
+
iframe = null,
|
|
106
|
+
name = "iframe-" + $.now(),
|
|
107
|
+
files = $(options.files).filter(":file:enabled"),
|
|
108
|
+
markers = null,
|
|
109
|
+
accepts = null;
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
// This function gets called after a successful submission or an abortion
|
|
113
|
+
// and should revert all changes made to the page to enable the
|
|
114
|
+
// submission via this transport.
|
|
115
|
+
function cleanUp() {
|
|
116
|
+
files.each(function(i, file) {
|
|
117
|
+
var $file = $(file);
|
|
118
|
+
$file.data("clone").replaceWith($file);
|
|
119
|
+
});
|
|
120
|
+
form.remove();
|
|
121
|
+
iframe.one("load", function() { iframe.remove(); });
|
|
122
|
+
iframe.attr("src", "about:blank");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remove "iframe" from the data types list so that further processing is
|
|
126
|
+
// based on the content type returned by the server, without attempting an
|
|
127
|
+
// (unsupported) conversion from "iframe" to the actual type.
|
|
128
|
+
options.dataTypes.shift();
|
|
129
|
+
|
|
130
|
+
// Use the data from the original AJAX options, as it doesn't seem to be
|
|
131
|
+
// copied over since jQuery 1.7.
|
|
132
|
+
// See https://github.com/cmlenz/jquery-iframe-transport/issues/6
|
|
133
|
+
options.data = origOptions.data;
|
|
134
|
+
|
|
135
|
+
if (files.length) {
|
|
136
|
+
form = $("<form enctype='multipart/form-data' method='post'></form>").
|
|
137
|
+
hide().attr({action: options.originalURL, target: name});
|
|
138
|
+
|
|
139
|
+
// If there is any additional data specified via the `data` option,
|
|
140
|
+
// we add it as hidden fields to the form. This (currently) requires
|
|
141
|
+
// the `processData` option to be set to false so that the data doesn't
|
|
142
|
+
// get serialized to a string.
|
|
143
|
+
if (typeof(options.data) === "string" && options.data.length > 0) {
|
|
144
|
+
$.error("data must not be serialized");
|
|
145
|
+
}
|
|
146
|
+
$.each(options.data || {}, function(name, value) {
|
|
147
|
+
if ($.isPlainObject(value)) {
|
|
148
|
+
name = value.name;
|
|
149
|
+
value = value.value;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
$("<input type='hidden' />").attr({name: name, value: value}).
|
|
154
|
+
appendTo(form);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Add a hidden `X-Requested-With` field with the value `IFrame` to the
|
|
158
|
+
// field, to help server-side code to determine that the upload happened
|
|
159
|
+
// through this transport.
|
|
160
|
+
$("<input type='hidden' value='IFrame' name='X-Requested-With' />").
|
|
161
|
+
appendTo(form);
|
|
162
|
+
|
|
163
|
+
// ----------------------------------------------------------------------
|
|
164
|
+
// Modified section
|
|
165
|
+
// ----------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
// Add CSRF token to the form since jquery-ujs adds it to the XHR request
|
|
168
|
+
// headers and the iframe transport doesn't support them
|
|
169
|
+
$("<input type='hidden' />").attr({name: $.rails.csrfParam(), value: $.rails.csrfToken()}).
|
|
170
|
+
appendTo(form);
|
|
171
|
+
|
|
172
|
+
// ----------------------------------------------------------------------
|
|
173
|
+
// End of modified section
|
|
174
|
+
// ----------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
// Borrowed straight from the JQuery source.
|
|
177
|
+
// Provides a way of specifying the accepted data type similar to the
|
|
178
|
+
// HTTP "Accept" header
|
|
179
|
+
if (options.dataTypes[0] && options.accepts[options.dataTypes[0]]) {
|
|
180
|
+
accepts = options.accepts[options.dataTypes[0]] +
|
|
181
|
+
(options.dataTypes[0] !== "*" ? ", */*; q=0.01" : "");
|
|
182
|
+
} else {
|
|
183
|
+
accepts = options.accepts["*"];
|
|
184
|
+
}
|
|
185
|
+
$("<input type='hidden' name='X-HTTP-Accept'>").
|
|
186
|
+
attr("value", accepts).appendTo(form);
|
|
187
|
+
|
|
188
|
+
// Move the file fields into the hidden form, but first remember their
|
|
189
|
+
// original locations in the document by replacing them with disabled
|
|
190
|
+
// clones. This should also avoid introducing unwanted changes to the
|
|
191
|
+
// page layout during submission.
|
|
192
|
+
markers = files.after(function(idx) {
|
|
193
|
+
var $this = $(this),
|
|
194
|
+
$clone = $this.clone().prop("disabled", true);
|
|
195
|
+
$this.data("clone", $clone);
|
|
196
|
+
return $clone;
|
|
197
|
+
}).next();
|
|
198
|
+
files.appendTo(form);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
|
|
202
|
+
// The `send` function is called by jQuery when the request should be
|
|
203
|
+
// sent.
|
|
204
|
+
send: function(headers, completeCallback) {
|
|
205
|
+
iframe = $("<iframe src='about:blank' name='" + name +
|
|
206
|
+
"' id='" + name + "' style='display:none'></iframe>");
|
|
207
|
+
|
|
208
|
+
// The first load event gets fired after the iframe has been injected
|
|
209
|
+
// into the DOM, and is used to prepare the actual submission.
|
|
210
|
+
iframe.one("load", function() {
|
|
211
|
+
|
|
212
|
+
// The second load event gets fired when the response to the form
|
|
213
|
+
// submission is received. The implementation detects whether the
|
|
214
|
+
// actual payload is embedded in a `<textarea>` element, and
|
|
215
|
+
// prepares the required conversions to be made in that case.
|
|
216
|
+
iframe.one("load", function() {
|
|
217
|
+
var doc = this.contentWindow ? this.contentWindow.document :
|
|
218
|
+
(this.contentDocument ? this.contentDocument : this.document),
|
|
219
|
+
root = doc.documentElement ? doc.documentElement : doc.body,
|
|
220
|
+
dataInput = $(root).find("[data-iframe-response-data]").remove()[0],
|
|
221
|
+
type = dataInput && dataInput.getAttribute("data-type") || null,
|
|
222
|
+
status = (dataInput && dataInput.getAttribute("data-status")) || 200,
|
|
223
|
+
statusText = dataInput && dataInput.getAttribute("data-statusText") || "OK",
|
|
224
|
+
content = {
|
|
225
|
+
html: root.innerHTML,
|
|
226
|
+
text: root ? (root.textContent || root.innerText) : null
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
jqXHR.responseText = content.html;
|
|
230
|
+
|
|
231
|
+
cleanUp();
|
|
232
|
+
completeCallback(status, statusText, content, type ?
|
|
233
|
+
("Content-Type: " + type) :
|
|
234
|
+
null);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Now that the load handler has been set up, submit the form.
|
|
238
|
+
form[0].submit();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// After everything has been set up correctly, the form and iframe
|
|
242
|
+
// get injected into the DOM so that the submission can be
|
|
243
|
+
// initiated.
|
|
244
|
+
$("body").append(form, iframe);
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// The `abort` function is called by jQuery when the request should be
|
|
248
|
+
// aborted.
|
|
249
|
+
abort: function() {
|
|
250
|
+
if (iframe !== null) {
|
|
251
|
+
iframe.unbind("load").attr("src", "about:blank");
|
|
252
|
+
cleanUp();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
})(jQuery);
|