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