cocooned 1.3.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.
@@ -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