cocooned 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,101 @@
1
+ Cocooned.Plugins.Reorderable = {
2
+
3
+ defaultOptionValue: true,
4
+
5
+ bindReorderable: function() {
6
+ var self = this;
7
+
8
+ // Maintain indexes
9
+ this.container
10
+ .on('cocooned:after-insert', function(e) { self.reindex(); })
11
+ .on('cocooned:after-remove', function(e) { self.reindex(); })
12
+ .on('cocooned:after-move', function(e) { self.reindex(); });
13
+
14
+ // Move items
15
+ this.container.on(
16
+ this.namespacedNativeEvents('click'),
17
+ [this.selector('up'), this.selector('down')].join(', '),
18
+ function(e) {
19
+ e.preventDefault();
20
+ var node = this;
21
+ var up = self.classes['up'].some(function(klass) {
22
+ return node.className.indexOf(klass) != -1;
23
+ });
24
+ self.move(this, up ? 'up' : 'down');
25
+ });
26
+
27
+ // Ensure positions are unique before save
28
+ this.container.closest('form').on(
29
+ this.namespacedNativeEvents('submit'),
30
+ function(e) {
31
+ self.reindex();
32
+ });
33
+ },
34
+
35
+ move: function(moveLink, direction) {
36
+ var self = this;
37
+ var $mover = $(moveLink);
38
+ var node = $mover.closest(this.selector('item'));
39
+ var siblings = (direction == 'up'
40
+ ? node.prevAll(this.selector('item', '&:eq(0)'))
41
+ : node.nextAll(this.selector('item', '&:eq(0)')));
42
+
43
+ if (siblings.length == 0) {
44
+ return;
45
+ }
46
+
47
+ // Move can be prevented through a 'cocooned:before-move' event handler
48
+ var eventData = { link: $mover, node: node, cocooned: this };
49
+ if (!self.notify(node, 'before-move', eventData)) {
50
+ return false;
51
+ }
52
+
53
+ var height = self.container.outerHeight();
54
+ var width = self.container.outerWidth();
55
+
56
+ self.container.css('height', height).css('width', width);
57
+ self.hide(node, function() {
58
+ var movedNode = $(this).detach();
59
+ movedNode[(direction == 'up' ? 'insertBefore' : 'insertAfter')](siblings);
60
+
61
+ self.show(movedNode, function() {
62
+ self.container.css('height', '').css('width', ''); // Object notation does not work here.
63
+ self.notify(movedNode, 'after-move', eventData);
64
+ });
65
+ });
66
+ },
67
+
68
+ reindex: function() {
69
+ var self = this;
70
+ var i = 0;
71
+ var nodes = this.getItems('&:visible');
72
+ var eventData = { link: null, nodes: nodes, cocooned: this };
73
+
74
+ // Reindex can be prevented through a 'cocooned:before-reindex' event handler
75
+ if (!this.notify(this.container, 'before-reindex', eventData)) {
76
+ return false;
77
+ }
78
+
79
+ nodes.each(function() { $('input[id$=_position]', this).val(++i); });
80
+ this.notify(this.container, 'after-reindex', eventData);
81
+ },
82
+
83
+ show: function(node, callback) {
84
+ callback = callback || function() {};
85
+
86
+ node.addClass('cocooned-visible-item');
87
+ setTimeout(function() {
88
+ callback.apply(node);
89
+ node.removeClass('cocooned-hidden-item');
90
+ }, 500);
91
+ },
92
+
93
+ hide: function(node, callback) {
94
+ node.removeClass('cocooned-visible-item').addClass('cocooned-hidden-item');
95
+ if (callback) {
96
+ setTimeout(function() {
97
+ callback.apply(node);
98
+ }, 500);
99
+ }
100
+ }
101
+ };
@@ -0,0 +1,3 @@
1
+ //= require './cocooned/core'
2
+ //= require_tree './cocooned/plugins'
3
+ //= require_tree './cocooned/jquery'
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require cocooned
3
+ */
@@ -0,0 +1,9 @@
1
+
2
+ .cocooned-visible-item {
3
+ transition: opacity .45s ease-out;
4
+ opacity: 1;
5
+ }
6
+
7
+ .cocooned-hidden-item {
8
+ opacity: 0;
9
+ }
data/cocooned.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'cocooned/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'cocooned'
9
+ spec.version = Cocooned::VERSION
10
+ spec.licenses = ['Apache-2.0']
11
+ spec.authors = ['Gaël-Ian Havard', 'Nathan Van der Auwera']
12
+ spec.email = ['gael-ian@notus.sh', 'nathan@dixis.com']
13
+
14
+ spec.summary = 'Unobtrusive nested forms handling using jQuery.'
15
+ spec.description = 'Easier nested form. Supports standard Rails forms, Formtastic and SimpleForm.'
16
+ spec.homepage = 'http://github.com/notus-sh/cocooned'
17
+
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ else
21
+ raise 'RubyGems 2.0 or newer is required.'
22
+ end
23
+
24
+ spec.require_paths = ['lib']
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+
29
+ spec.add_dependency 'rails', '>= 4.0', '<= 6.0'
30
+
31
+ spec.add_development_dependency 'bundler', '~> 1.16'
32
+ spec.add_development_dependency 'jasmine', '~> 3.2'
33
+ spec.add_development_dependency 'rake'
34
+ spec.add_development_dependency 'rspec', '~> 3.8.0'
35
+ spec.add_development_dependency 'rspec-rails', '~> 3.8.0'
36
+ spec.add_development_dependency 'rubocop'
37
+ end
@@ -0,0 +1,50 @@
1
+ {
2
+ "extends": "standard",
3
+ "env": {
4
+ "browser": true,
5
+ "jquery": true
6
+ },
7
+ "parserOptions": { "ecmaVersion": 5 },
8
+ "rules": {
9
+ // Force strict mode
10
+ "strict": ["error", "function"],
11
+
12
+ // Coding style
13
+ "semi": ["error", "always"], // Keep semi-colon, even if standard say we shouldn't
14
+ "semi-style": ["error", "last"],
15
+ "object-curly-newline": ["error", { "consistent": true }],
16
+ "object-curly-spacing": ["error", "always"],
17
+ "switch-colon-spacing": ["error"],
18
+ "linebreak-style": ["error"],
19
+
20
+ // Warn on code smells
21
+ "max-len": ["warn", { "code": 150 }],
22
+ "max-params": ["warn"],
23
+
24
+ // No dead code
25
+ "no-else-return": ["error"],
26
+ "no-empty-function": ["error"],
27
+ "no-empty-pattern": ["error"],
28
+ "no-unused-expressions": ["error"],
29
+ "no-lonely-if": ["error"],
30
+
31
+ // Prevent stupid errors
32
+ "array-callback-return": ["error"],
33
+ "guard-for-in": ["error"],
34
+ "no-invalid-this": ["error"],
35
+ "no-loop-func": ["error"],
36
+ "radix": ["error"],
37
+ "no-catch-shadow": ["error"],
38
+ "no-use-before-define": ["error"],
39
+
40
+ // Prevent stupid syntaxes
41
+ "no-useless-concat": ["error"],
42
+ "no-useless-return": ["error"],
43
+
44
+ // No debug code will be commited
45
+ "no-alert": ["error"],
46
+
47
+ // eslint don't know the context
48
+ "no-invalid-this": ["off"]
49
+ }
50
+ }
@@ -0,0 +1,16 @@
1
+ inherit_mode:
2
+ merge:
3
+ - Exclude
4
+
5
+ AllCops:
6
+ Exclude:
7
+ - 'spec/dummy/bin/*'
8
+
9
+ Metrics/LineLength:
10
+ Max: 150
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - 'spec/dummy/db/schema.rb'
15
+ - 'spec/cocooned/**/*_spec.rb'
16
+ - 'spec/shared_examples/**/*.rb'
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ group :development, :test do
4
+ gem 'rails', '~> 4.0'
5
+ gem 'sqlite3'
6
+ gem 'simplecov', :require => false
7
+ gem 'nokogiri'
8
+ end
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ group :development, :test do
4
+ gem 'rails', '~> 5.0'
5
+ gem 'sqlite3'
6
+ gem 'simplecov', :require => false
7
+ gem 'nokogiri'
8
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocooned
4
+ class AssociationBuilder
5
+ attr_accessor :association
6
+ attr_accessor :form
7
+ attr_accessor :options
8
+
9
+ def initialize(form, association, options = {})
10
+ self.form = form
11
+ self.association = association
12
+ self.options = options.reverse_merge(force_non_association_create: false, wrap_object: false)
13
+ end
14
+
15
+ def build_object
16
+ model = reflection ? build_with_reflection : build_without_reflection
17
+ model = @options[:wrap_object].call(model) if @options[:wrap_object].respond_to?(:call)
18
+ model
19
+ end
20
+
21
+ def singular_name
22
+ association.to_s.singularize
23
+ end
24
+
25
+ def plural_name
26
+ association.to_s.pluralize
27
+ end
28
+
29
+ private
30
+
31
+ def reflection
32
+ @reflection ||= begin
33
+ klass = form.object.class
34
+ klass.respond_to?(:reflect_on_association) ? klass.reflect_on_association(association) : nil
35
+ end
36
+ end
37
+
38
+ def build_with_reflection
39
+ return build_with_conditions if should_use_conditions?
40
+
41
+ # Assume ActiveRecord or compatible
42
+ # We use a clone of the current form object to not link
43
+ # object together (even if unsaved)
44
+ dummy = form.object.dup
45
+ model = if reflection.collection?
46
+ dummy.send(association).build
47
+ else
48
+ dummy.send("build_#{association}")
49
+ end
50
+ model = model.dup if model.frozen?
51
+ model
52
+ end
53
+
54
+ def build_without_reflection
55
+ methods = %W[build_#{plural_name} build_#{singular_name}].select { |m| form.object.respond_to?(m) }
56
+ raise "Association #{association} doesn't exist on #{form.object.class}" unless methods.any?
57
+ form.object.send(methods.first)
58
+ end
59
+
60
+ def should_use_conditions?
61
+ reflection.class.name == 'Mongoid::Relations::Metadata' || @options[:force_non_association_create]
62
+ end
63
+
64
+ def build_with_conditions
65
+ conditions = reflection.respond_to?(:conditions) ? reflection.conditions.flatten : []
66
+ reflection.klass.new(*conditions)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocooned/helpers/deprecate'
4
+
5
+ module Cocooned
6
+ module Helpers
7
+ # Provide aliases to old Cocoon method for backward compatibility.
8
+ # Cocoon methods are deprecated and will be removed in next major release.
9
+ #
10
+ # TODO: Remove in 2.0
11
+ module CocoonCompatibility
12
+ extend Cocooned::Helpers::Deprecate
13
+
14
+ # @deprecated: Please use {#cocooned_add_item_link} instead
15
+ def link_to_add_association(*args, &block)
16
+ cocooned_add_item_link(*args, &block)
17
+ end
18
+ deprecate_release :link_to_add_association, :cocooned_add_item_link
19
+
20
+ # @deprecated: Please use {#cocooned_remove_item_link} instead
21
+ def link_to_remove_association(*args, &block)
22
+ cocooned_remove_item_link(*args, &block)
23
+ end
24
+ deprecate_release :link_to_remove_association, :cocooned_remove_item_link
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems/deprecate'
4
+
5
+ module Cocooned
6
+ module Helpers
7
+ # Extend the standard Gem::Deprecation module to add a deprecation
8
+ # method that specify the gem release where methods will disappear
9
+ # instead of a date.
10
+ module Deprecate
11
+ extend Gem::Deprecate
12
+
13
+ def deprecate_release_message(target_and_name, replacement, release = '2.0', location = nil)
14
+ [
15
+ "NOTE: #{target_and_name} is deprecated",
16
+ replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
17
+ format('. It will dissapear in %s.', release),
18
+ location.nil? ? '' : "\n#{target_and_name} called from #{location}"
19
+ ].join.strip
20
+ end
21
+
22
+ module_function :deprecate_release_message
23
+
24
+ def deprecate_release(name, replacement, release = '2.0')
25
+ class_eval do
26
+ old = "_deprecated_#{name}"
27
+ alias_method old, name
28
+ define_method name do |*args, &block|
29
+ klass = is_a? Module
30
+ target = klass ? "#{self}." : "#{self.class}#"
31
+
32
+ unless Gem::Deprecate.skip
33
+ warn(deprecate_release_message(
34
+ "#{target}#{name}",
35
+ replacement,
36
+ release,
37
+ Gem.location_of_caller.join(':')
38
+ ))
39
+ end
40
+
41
+ send old, *args, &block
42
+ end
43
+ end
44
+ end
45
+
46
+ module_function :deprecate_release
47
+ end
48
+ end
49
+ end