awesome_nested_fields 0.0.3
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.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/app/helpers/awesome_nested_fields_helper.rb +46 -0
- data/awesome_nested_fields.gemspec +23 -0
- data/lib/awesome_nested_fields.rb +11 -0
- data/lib/awesome_nested_fields/version.rb +3 -0
- data/public/javascripts/jquery.nested-fields.js +195 -0
- metadata +107 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module AwesomeNestedFieldsHelper
|
2
|
+
def nested_fields_items(builder, association, options={})
|
3
|
+
options = nested_fields_process_default_options(options, builder, association)
|
4
|
+
|
5
|
+
items = ''
|
6
|
+
builder.fields_for(association) do |f|
|
7
|
+
items << render(options[:partial], options[:builder_local] => f)
|
8
|
+
end
|
9
|
+
|
10
|
+
if options[:none_partial] and builder.object.send(association).empty?
|
11
|
+
items << render(options[:none_partial], options[:builder_local] => f)
|
12
|
+
end
|
13
|
+
|
14
|
+
items.html_safe
|
15
|
+
end
|
16
|
+
|
17
|
+
def nested_fields_template(builder, association, options={})
|
18
|
+
options = nested_fields_process_default_options(options, builder, association)
|
19
|
+
|
20
|
+
templates = content_tag(:script, type: 'text/html', class: options[:item_template_class]) do
|
21
|
+
builder.fields_for(association, options[:new_object], child_index: 'new_nested_item') do |f|
|
22
|
+
render(options[:partial], options[:builder_local] => f)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:none_partial]
|
27
|
+
templates << content_tag(:script, type: 'text/html', class: options[:none_template_class]) do
|
28
|
+
builder.fields_for(association, options[:new_object], child_index: 'new_nested_item') do |f|
|
29
|
+
render(options[:none_partial], options[:builder_local] => f)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
templates.html_safe
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def nested_fields_process_default_options(options, builder, association)
|
39
|
+
options[:new_object] ||= builder.object.class.reflect_on_association(association).klass.new
|
40
|
+
options[:partial] ||= association.to_s.singularize
|
41
|
+
options[:builder_local] ||= :builder
|
42
|
+
options[:item_template_class] ||= 'template item'
|
43
|
+
options[:none_template_class] ||= 'template none'
|
44
|
+
options
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/awesome_nested_fields/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'awesome_nested_fields'
|
6
|
+
s.version = AwesomeNestedFields::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = %q[Lailson Bandeira]
|
9
|
+
s.email = %q[lailson@guava.com.br]
|
10
|
+
s.homepage = 'http://rubygems.org/gems/awesome_nested_fields'
|
11
|
+
s.summary = 'Awesome nested fields for Rails'
|
12
|
+
s.description = 'Awesome dynamic nested fields for Rails and jQuery'
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "awesome_nested_fields"
|
16
|
+
|
17
|
+
s.add_development_dependency 'bundler', '>= 1.0.0'
|
18
|
+
s.add_runtime_dependency 'rails', '>= 3.0.0'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
22
|
+
s.require_path = 'lib'
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AwesomeNestedFields
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
initializer 'awesome_nested_fields.add_middleware' do |app|
|
4
|
+
app.middleware.use ActionDispatch::Static, "#{root}/public"
|
5
|
+
end
|
6
|
+
|
7
|
+
config.to_prepare do
|
8
|
+
ApplicationController.helper(AwesomeNestedFieldsHelper)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
(function($) {
|
2
|
+
|
3
|
+
var defaultSettings = {
|
4
|
+
beforeInsert: function(item, callback) { callback() },
|
5
|
+
afterInsert: function(item) {},
|
6
|
+
beforeRemove: function(item, callback) { callback() },
|
7
|
+
afterRemove: function(item) {},
|
8
|
+
itemTemplate: '.item.template',
|
9
|
+
noneTemplate: '.none.template',
|
10
|
+
container: '.container',
|
11
|
+
item: '.item',
|
12
|
+
none: '.none',
|
13
|
+
addHandler: '.add',
|
14
|
+
removeHandler: '.remove',
|
15
|
+
newItemIndex: 'new_nested_item'
|
16
|
+
};
|
17
|
+
|
18
|
+
// PUBLIC API
|
19
|
+
var methods = {
|
20
|
+
init: function(options) {
|
21
|
+
var $this = $(this);
|
22
|
+
if($(this).data('nested-fields.options')) {
|
23
|
+
console.log('Nested fields already defined for this element. If you want to redefine options, destroy it and init again.');
|
24
|
+
return $this;
|
25
|
+
} else if(getOptions($this)) {
|
26
|
+
console.log('You cannot nest nested fields. Who would say that, uh?');
|
27
|
+
return $this;
|
28
|
+
}
|
29
|
+
|
30
|
+
options = $.extend({}, defaultSettings, options);
|
31
|
+
options.itemTemplate = $(options.itemTemplate, $this);
|
32
|
+
options.noneTemplate = $(options.noneTemplate, $this);
|
33
|
+
options.container = $(options.container, $this);
|
34
|
+
options.addHandler = $(options.addHandler, $this);
|
35
|
+
$this.data('nested-fields.options', options);
|
36
|
+
|
37
|
+
options.addHandler.bind('click.nested-fields', function(e) {
|
38
|
+
e.preventDefault();
|
39
|
+
var newItem = prepareTemplate(options);
|
40
|
+
insertItemWithCallbacks(newItem, null, options);
|
41
|
+
});
|
42
|
+
|
43
|
+
$(options.item, options.container).each(function(i, item) {
|
44
|
+
bindRemoveEvent(item, options);
|
45
|
+
});
|
46
|
+
|
47
|
+
return $this;
|
48
|
+
},
|
49
|
+
|
50
|
+
insert: function(callback, options) {
|
51
|
+
options = $.extend({}, getOptions(this), options);
|
52
|
+
var newItem = prepareTemplate(options);
|
53
|
+
|
54
|
+
insertItemWithCallbacks(newItem, callback, options);
|
55
|
+
},
|
56
|
+
|
57
|
+
remove: function(element, options) {
|
58
|
+
options = $.extend({}, getOptions(this), options);
|
59
|
+
return removeItem(element, options);
|
60
|
+
},
|
61
|
+
|
62
|
+
removeAll: function(options) {
|
63
|
+
options = $.extend({}, getOptions(this), options);
|
64
|
+
$(methods.items.apply(this)).each(function(i, el) {
|
65
|
+
methods.remove(el, options);
|
66
|
+
});
|
67
|
+
},
|
68
|
+
|
69
|
+
items: function() {
|
70
|
+
return findItems(getOptions(this));
|
71
|
+
},
|
72
|
+
|
73
|
+
destroy: function() {
|
74
|
+
$(this).removeData('nested-fields.options');
|
75
|
+
$('*', this).unbind('.nested-fields');
|
76
|
+
}
|
77
|
+
};
|
78
|
+
|
79
|
+
$.fn.nestedFields = function(method) {
|
80
|
+
if (methods[method]) {
|
81
|
+
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
82
|
+
} else if (typeof method === 'object' || !method) {
|
83
|
+
return methods.init.apply(this, arguments);
|
84
|
+
} else {
|
85
|
+
$.error( 'Method ' + method + ' does not exist on jQuery.nestedFields' );
|
86
|
+
}
|
87
|
+
};
|
88
|
+
|
89
|
+
function getOptions(element) {
|
90
|
+
element = $(element);
|
91
|
+
while(element.length > 0) {
|
92
|
+
var data = element.data('nested-fields.options');
|
93
|
+
if(data) {
|
94
|
+
return data;
|
95
|
+
} else {
|
96
|
+
element = element.parent();
|
97
|
+
}
|
98
|
+
}
|
99
|
+
return null;
|
100
|
+
}
|
101
|
+
|
102
|
+
function prepareTemplate(options) {
|
103
|
+
var regexp = new RegExp(options.newItemIndex, 'g');
|
104
|
+
var newId = new Date().getTime();
|
105
|
+
|
106
|
+
var contents = options.itemTemplate.html();
|
107
|
+
var newItem = $(contents.replace(regexp, newId));
|
108
|
+
newItem.attr('data-new-record', true);
|
109
|
+
newItem.attr('data-record-id', newId);
|
110
|
+
|
111
|
+
bindRemoveEvent(newItem, options);
|
112
|
+
|
113
|
+
return newItem;
|
114
|
+
}
|
115
|
+
|
116
|
+
function insertItem(newItem, options) {
|
117
|
+
removeNone(options);
|
118
|
+
options.container.append(newItem);
|
119
|
+
}
|
120
|
+
|
121
|
+
function insertItemWithCallbacks(newItem, onInsertCallback, options) {
|
122
|
+
function insert() {
|
123
|
+
if(onInsertCallback) {
|
124
|
+
onInsertCallback(newItem);
|
125
|
+
}
|
126
|
+
insertItem(newItem, options);
|
127
|
+
}
|
128
|
+
|
129
|
+
if(!options.skipBefore) {
|
130
|
+
options.beforeInsert(newItem, insert);
|
131
|
+
} else {
|
132
|
+
insert();
|
133
|
+
}
|
134
|
+
|
135
|
+
if(!options.skipAfter) {
|
136
|
+
options.afterInsert(newItem);
|
137
|
+
}
|
138
|
+
|
139
|
+
return newItem;
|
140
|
+
}
|
141
|
+
|
142
|
+
function removeItem(element, options) {
|
143
|
+
function remove() {
|
144
|
+
if($element.attr('data-new-record')) { // record is new
|
145
|
+
$element.remove();
|
146
|
+
} else { // record should be marked and sent to server
|
147
|
+
$element.find("INPUT[name$='[_destroy]']").val('true');
|
148
|
+
$element.hide();
|
149
|
+
}
|
150
|
+
insertNone(options);
|
151
|
+
}
|
152
|
+
|
153
|
+
var $element = $(element);
|
154
|
+
if(!options.skipBefore) {
|
155
|
+
options.beforeRemove($element, remove);
|
156
|
+
} else {
|
157
|
+
remove();
|
158
|
+
}
|
159
|
+
|
160
|
+
if(!options.skipAfter) {
|
161
|
+
options.afterRemove($element);
|
162
|
+
}
|
163
|
+
|
164
|
+
return $element;
|
165
|
+
}
|
166
|
+
|
167
|
+
function bindRemoveEvent(item, options) {
|
168
|
+
var removeHandler = $(item).find(options.removeHandler);
|
169
|
+
var needsConfirmation = removeHandler.attr('data-confirm');
|
170
|
+
|
171
|
+
var event = needsConfirmation ? 'confirmed' : 'click';
|
172
|
+
removeHandler.bind(event + '.nested-fields', function(e) {
|
173
|
+
removeItem(item, options);
|
174
|
+
});
|
175
|
+
}
|
176
|
+
|
177
|
+
function insertNone(options) {
|
178
|
+
if(findItems(options).length == 0) {
|
179
|
+
options.container.append(options.noneTemplate.html());
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
function removeNone(options) {
|
184
|
+
findNone(options).remove();
|
185
|
+
}
|
186
|
+
|
187
|
+
function findItems(options) {
|
188
|
+
return options.container.find(options.item + ':visible');
|
189
|
+
}
|
190
|
+
|
191
|
+
function findNone(options) {
|
192
|
+
return options.container.find(options.none);
|
193
|
+
}
|
194
|
+
|
195
|
+
})(jQuery);
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: awesome_nested_fields
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Lailson Bandeira
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-12 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rails
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 3
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 3.0.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Awesome dynamic nested fields for Rails and jQuery
|
54
|
+
email: lailson@guava.com.br
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files: []
|
60
|
+
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- Gemfile
|
64
|
+
- Rakefile
|
65
|
+
- app/helpers/awesome_nested_fields_helper.rb
|
66
|
+
- awesome_nested_fields.gemspec
|
67
|
+
- lib/awesome_nested_fields.rb
|
68
|
+
- lib/awesome_nested_fields/version.rb
|
69
|
+
- public/javascripts/jquery.nested-fields.js
|
70
|
+
has_rdoc: true
|
71
|
+
homepage: http://rubygems.org/gems/awesome_nested_fields
|
72
|
+
licenses: []
|
73
|
+
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 23
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 3
|
97
|
+
- 6
|
98
|
+
version: 1.3.6
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project: awesome_nested_fields
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Awesome nested fields for Rails
|
106
|
+
test_files: []
|
107
|
+
|