cocooned 1.3.0 → 1.3.1
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/Rakefile +76 -14
- data/app/assets/javascripts/cocoon.js +1 -0
- data/app/assets/javascripts/cocooned.js +449 -3
- data/app/assets/stylesheets/cocoon.css +2 -0
- data/cocooned.gemspec +3 -1
- data/lib/cocooned/version.rb +1 -1
- metadata +2 -18
- data/.gitignore +0 -15
- data/.rspec +0 -2
- data/.travis.yml +0 -14
- data/Gemfile +0 -16
- data/Gemfile.lock +0 -194
- data/app/assets/javascripts/cocooned/core.js +0 -284
- data/app/assets/javascripts/cocooned/jquery/onload.js +0 -10
- data/app/assets/javascripts/cocooned/jquery/plugin.js +0 -20
- data/app/assets/javascripts/cocooned/plugins/limit.js +0 -22
- data/app/assets/javascripts/cocooned/plugins/reorderable.js +0 -101
- data/config/linters/js.json +0 -50
- data/config/linters/ruby.yml +0 -16
- data/gemfiles/Gemfile.rails-4 +0 -8
- data/gemfiles/Gemfile.rails-5 +0 -8
- data/package.json +0 -24
- data/yarn.lock +0 -1052
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ff5fc052a1bf37f18e1f22b550f35bf876e0cc09c4c7d4ebef3667c7afde67a
|
4
|
+
data.tar.gz: 55202ea14bda345f2ed5f20fc95ebb0de1bfa7831e9f70ba92b2a092c7ca210b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed672de23fce13326d63cb1e943689ea8f4df8be8873111daecb9a25df954ee06a47592a6b28399c95120e6079337c26125838b42a868395e0d5639d08461569
|
7
|
+
data.tar.gz: 23b8d0ab6bdfda55e974f322bec4a0a39a9fd089f9bfbfa343144ccdc76639560286a4212ac3018e0ee10a567d70b8f363be5c3c3578e5000440de41a2464e04
|
data/Rakefile
CHANGED
@@ -1,44 +1,106 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
## Test suites
|
4
4
|
|
5
|
-
#
|
5
|
+
# Ruby
|
6
6
|
require 'rspec/core/rake_task'
|
7
7
|
RSpec::Core::RakeTask.new
|
8
|
-
|
9
8
|
task default: :spec
|
10
9
|
|
10
|
+
# JavaScript
|
11
11
|
require 'jasmine'
|
12
12
|
load 'jasmine/tasks/jasmine.rake'
|
13
13
|
|
14
|
-
|
14
|
+
## Linters
|
15
|
+
|
16
|
+
# Ruby
|
15
17
|
require 'rubocop/rake_task'
|
16
18
|
RuboCop::RakeTask.new do |task|
|
17
19
|
task.options = ['--config', 'config/linters/ruby.yml']
|
18
20
|
end
|
19
21
|
|
22
|
+
# JavaScript
|
20
23
|
eslint_args = ['--no-eslintrc', '--config config/linters/js.json']
|
21
24
|
eslint_path = ['app/assets/**/*.js', 'spec/javascripts/**/*.js', 'spec/dummy/app/assets/**/*.js']
|
22
25
|
|
23
26
|
namespace :eslint do
|
24
|
-
desc 'Auto-correct
|
27
|
+
desc 'Auto-correct eslint offenses'
|
25
28
|
task :auto_correct do
|
26
29
|
system("yarnpkg run eslint #{(eslint_args + ['--fix']).join(' ')} #{eslint_path.join(' ')}")
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
30
|
-
desc '
|
33
|
+
desc 'Run eslint'
|
31
34
|
task :eslint do
|
32
35
|
system("yarnpkg run eslint #{eslint_args.join(' ')} #{eslint_path.join(' ')}")
|
33
36
|
end
|
34
37
|
|
35
|
-
#
|
36
|
-
|
38
|
+
# Both
|
39
|
+
namespace :linters do
|
40
|
+
desc 'Auto-correct Rubocop and eslint offenses'
|
41
|
+
task auto_correct: ['rubocop:auto_correct', 'eslint:auto_correct']
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Run Rubocop and eslint'
|
45
|
+
task linters: %i[rubocop eslint]
|
46
|
+
|
47
|
+
## Packaging
|
48
|
+
|
49
|
+
# Ruby
|
50
|
+
require 'bundler/gem_tasks'
|
51
|
+
|
52
|
+
# JavaScript
|
53
|
+
require 'json'
|
54
|
+
|
55
|
+
spec = Bundler.load_gemspec('./cocooned.gemspec')
|
56
|
+
npm_scope = 'notus.sh'
|
57
|
+
|
58
|
+
npm_src_dir = './npm'
|
59
|
+
npm_dest_dir = './dist'
|
60
|
+
CLOBBER.include 'dist'
|
37
61
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
62
|
+
assets_dir = './app/assets/'
|
63
|
+
|
64
|
+
npm_files = {
|
65
|
+
File.join(npm_dest_dir, 'cocooned.js') => File.join(assets_dir, 'javascripts', 'cocooned.js'),
|
66
|
+
File.join(npm_dest_dir, 'cocooned.css') => File.join(assets_dir, 'stylesheets', 'cocooned.css'),
|
67
|
+
File.join(npm_dest_dir, 'README.md') => File.join(npm_src_dir, 'README.md'),
|
68
|
+
File.join(npm_dest_dir, 'LICENSE') => './LICENSE'
|
69
|
+
}
|
70
|
+
|
71
|
+
namespace :npm do
|
72
|
+
npm_files.each do |dest, src|
|
73
|
+
file dest => src do
|
74
|
+
cp src, dest
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
task :'package-json' do
|
79
|
+
contributors = []
|
80
|
+
spec.authors.each_with_index do |name, i|
|
81
|
+
next if spec.email[i].nil?
|
82
|
+
contributors << {
|
83
|
+
name: name.dup.force_encoding('UTF-8'),
|
84
|
+
email: spec.email[i].dup.force_encoding('UTF-8')
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
template = ERB.new(File.read(File.join(npm_src_dir, 'package.json.erb')))
|
89
|
+
content = template.result_with_hash(scope: npm_scope, spec: spec, contributors: contributors)
|
90
|
+
File.write(File.join(npm_dest_dir, 'package.json'),
|
91
|
+
JSON.pretty_generate(JSON.parse(content)))
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "Build #{npm_scope}-#{spec.name}-#{spec.version}.tgz into the pkg directory"
|
95
|
+
task build: (%i[package-json] + npm_files.keys) do
|
96
|
+
system("cd #{npm_dest_dir} && npm pack && mv ./#{npm_scope}-#{spec.name}-#{spec.version}.tgz ../pkg/")
|
97
|
+
end
|
98
|
+
|
99
|
+
desc "Build and push #{npm_scope}-#{spec.name}-#{spec.version}.tgz to https://npmjs.org"
|
100
|
+
task release: %i[build] do
|
101
|
+
system("npm publish ./pkg/#{npm_scope}-#{spec.name}-#{spec.version}.tgz --access public")
|
102
|
+
end
|
44
103
|
end
|
104
|
+
|
105
|
+
desc 'Build packages and push them to their respective repository'
|
106
|
+
task releases: [:release, 'npm:release']
|
@@ -1,3 +1,449 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
/* globals define */
|
2
|
+
|
3
|
+
(function (root, factory) {
|
4
|
+
if (typeof define === 'function' && define.amd) {
|
5
|
+
// AMD. Register as an anonymous module.
|
6
|
+
define(['jquery'], function (jquery) {
|
7
|
+
return (root.Cocooned = factory(jquery));
|
8
|
+
});
|
9
|
+
} else if (typeof module === 'object' && module.exports) {
|
10
|
+
// Node. Does not work with strict CommonJS, but
|
11
|
+
// only CommonJS-like environments that support module.exports,
|
12
|
+
// like Node.
|
13
|
+
module.exports = factory(require('jquery'));
|
14
|
+
} else {
|
15
|
+
// Browser globals
|
16
|
+
root.Cocooned = factory(root.jQuery);
|
17
|
+
}
|
18
|
+
}(typeof self !== 'undefined' ? self : this, function ($) {
|
19
|
+
var Cocooned = function (container, options) {
|
20
|
+
this.container = $(container);
|
21
|
+
this.options = $.extend({}, this.defaultOptions(), (options || {}));
|
22
|
+
|
23
|
+
// Autoload plugins
|
24
|
+
for (var moduleName in Cocooned.Plugins) {
|
25
|
+
if (Cocooned.Plugins.hasOwnProperty(moduleName)) {
|
26
|
+
var module = Cocooned.Plugins[moduleName];
|
27
|
+
var optionName = moduleName.charAt(0).toLowerCase() + moduleName.slice(1);
|
28
|
+
|
29
|
+
if (this.options[optionName]) {
|
30
|
+
for (var method in module) {
|
31
|
+
if (module.hasOwnProperty(method) && typeof module[method] === 'function') {
|
32
|
+
this[method] = module[method];
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
this.init();
|
40
|
+
};
|
41
|
+
|
42
|
+
Cocooned.Plugins = {};
|
43
|
+
Cocooned.prototype = {
|
44
|
+
|
45
|
+
elementsCounter: 0,
|
46
|
+
|
47
|
+
// Compatibility with Cocoon
|
48
|
+
// TODO: Remove in 2.0 (Only Cocoon namespaces).
|
49
|
+
namespaces: {
|
50
|
+
events: ['cocooned', 'cocoon']
|
51
|
+
},
|
52
|
+
|
53
|
+
// Compatibility with Cocoon
|
54
|
+
// TODO: Remove in 2.0 (Only Cocoon class names).
|
55
|
+
classes: {
|
56
|
+
// Actions link
|
57
|
+
add: ['cocooned-add', 'add_fields'],
|
58
|
+
remove: ['cocooned-remove', 'remove_fields'],
|
59
|
+
up: ['cocooned-move-up'],
|
60
|
+
down: ['cocooned-move-down'],
|
61
|
+
// Containers
|
62
|
+
container: ['cocooned-container'],
|
63
|
+
item: ['cocooned-item', 'nested-fields']
|
64
|
+
},
|
65
|
+
|
66
|
+
defaultOptions: function () {
|
67
|
+
var options = {};
|
68
|
+
|
69
|
+
for (var moduleName in Cocooned.Plugins) {
|
70
|
+
if (Cocooned.Plugins.hasOwnProperty(moduleName)) {
|
71
|
+
var module = Cocooned.Plugins[moduleName];
|
72
|
+
var optionName = moduleName.charAt(0).toLowerCase() + moduleName.slice(1);
|
73
|
+
|
74
|
+
options[optionName] = module.defaultOptionValue;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return options;
|
79
|
+
},
|
80
|
+
|
81
|
+
notify: function (node, eventType, eventData) {
|
82
|
+
return !(this.namespaces.events.some(function (namespace) {
|
83
|
+
var namespacedEventType = [namespace, eventType].join(':');
|
84
|
+
var event = $.Event(namespacedEventType, eventData);
|
85
|
+
|
86
|
+
node.trigger(event, [eventData.node, eventData.cocooned]);
|
87
|
+
|
88
|
+
return (event.isPropagationStopped() || event.isDefaultPrevented());
|
89
|
+
}));
|
90
|
+
},
|
91
|
+
|
92
|
+
selector: function (type, selector) {
|
93
|
+
var s = selector || '&';
|
94
|
+
return this.classes[type].map(function (klass) { return s.replace(/&/, '.' + klass); }).join(', ');
|
95
|
+
},
|
96
|
+
|
97
|
+
namespacedNativeEvents: function (type) {
|
98
|
+
var namespaces = this.namespaces.events.map(function (ns) { return '.' + ns; });
|
99
|
+
namespaces.unshift(type);
|
100
|
+
return namespaces.join('');
|
101
|
+
},
|
102
|
+
|
103
|
+
buildId: function () {
|
104
|
+
return (new Date().getTime() + this.elementsCounter++);
|
105
|
+
},
|
106
|
+
|
107
|
+
buildContentNode: function (content) {
|
108
|
+
var id = this.buildId();
|
109
|
+
var html = (content || this.content);
|
110
|
+
var braced = '[' + id + ']';
|
111
|
+
var underscored = '_' + id + '_';
|
112
|
+
|
113
|
+
['associations', 'association'].forEach(function (a) {
|
114
|
+
html = html.replace(this.regexps[a]['braced'], braced + '$1');
|
115
|
+
html = html.replace(this.regexps[a]['underscored'], underscored + '$1');
|
116
|
+
}, this);
|
117
|
+
|
118
|
+
return $(html);
|
119
|
+
},
|
120
|
+
|
121
|
+
getInsertionNode: function (adder) {
|
122
|
+
var $adder = $(adder);
|
123
|
+
var insertionNode = $adder.data('association-insertion-node');
|
124
|
+
var insertionTraversal = $adder.data('association-insertion-traversal');
|
125
|
+
|
126
|
+
if (!insertionNode) {
|
127
|
+
return $adder.parent();
|
128
|
+
}
|
129
|
+
|
130
|
+
if (typeof insertionNode === 'function') {
|
131
|
+
return insertionNode($adder);
|
132
|
+
}
|
133
|
+
|
134
|
+
if (insertionTraversal) {
|
135
|
+
return $adder[insertionTraversal](insertionNode);
|
136
|
+
}
|
137
|
+
|
138
|
+
return insertionNode === 'this' ? $adder : $(insertionNode);
|
139
|
+
},
|
140
|
+
|
141
|
+
getInsertionMethod: function (adder) {
|
142
|
+
var $adder = $(adder);
|
143
|
+
return $adder.data('association-insertion-method') || 'before';
|
144
|
+
},
|
145
|
+
|
146
|
+
getItems: function (selector) {
|
147
|
+
selector = selector || '';
|
148
|
+
var self = this;
|
149
|
+
return $(this.selector('item', selector), this.container).filter(function () {
|
150
|
+
return ($(this).closest(self.selector('container')).get(0) === self.container.get(0));
|
151
|
+
});
|
152
|
+
},
|
153
|
+
|
154
|
+
findContainer: function (addLink) {
|
155
|
+
var $adder = $(addLink);
|
156
|
+
var insertionNode = this.getInsertionNode($adder);
|
157
|
+
var insertionMethod = this.getInsertionMethod($adder);
|
158
|
+
|
159
|
+
switch (insertionMethod) {
|
160
|
+
case 'before':
|
161
|
+
case 'after':
|
162
|
+
case 'replaceWith':
|
163
|
+
return insertionNode.parent();
|
164
|
+
|
165
|
+
case 'append':
|
166
|
+
case 'prepend':
|
167
|
+
default:
|
168
|
+
return insertionNode;
|
169
|
+
}
|
170
|
+
},
|
171
|
+
|
172
|
+
findItem: function (removeLink) {
|
173
|
+
return $(removeLink).closest(this.selector('item'));
|
174
|
+
},
|
175
|
+
|
176
|
+
init: function () {
|
177
|
+
var self = this;
|
178
|
+
|
179
|
+
this.addLinks = $(this.selector('add')).filter(function () {
|
180
|
+
var container = self.findContainer(this);
|
181
|
+
return (container.get(0) === self.container.get(0));
|
182
|
+
});
|
183
|
+
|
184
|
+
var addLink = $(this.addLinks.get(0));
|
185
|
+
|
186
|
+
this.content = addLink.data('association-insertion-template');
|
187
|
+
this.regexps = {
|
188
|
+
association: {
|
189
|
+
braced: new RegExp('\\[new_' + addLink.data('association') + '\\](.*?\\s)', 'g'),
|
190
|
+
underscored: new RegExp('_new_' + addLink.data('association') + '_(\\w*)', 'g')
|
191
|
+
},
|
192
|
+
associations: {
|
193
|
+
braced: new RegExp('\\[new_' + addLink.data('associations') + '\\](.*?\\s)', 'g'),
|
194
|
+
underscored: new RegExp('_new_' + addLink.data('associations') + '_(\\w*)', 'g')
|
195
|
+
}
|
196
|
+
};
|
197
|
+
|
198
|
+
this.initUi();
|
199
|
+
this.bindEvents();
|
200
|
+
},
|
201
|
+
|
202
|
+
initUi: function () {
|
203
|
+
var self = this;
|
204
|
+
|
205
|
+
if (!this.container.attr('id')) {
|
206
|
+
this.container.attr('id', this.buildId());
|
207
|
+
}
|
208
|
+
this.container.addClass(this.classes['container'].join(' '));
|
209
|
+
|
210
|
+
$(function () { self.hideMarkedForDestruction(); });
|
211
|
+
$(document).on('page:load turbolinks:load', function () { self.hideMarkedForDestruction(); });
|
212
|
+
},
|
213
|
+
|
214
|
+
bindEvents: function () {
|
215
|
+
var self = this;
|
216
|
+
|
217
|
+
// Bind add links
|
218
|
+
this.addLinks.on(
|
219
|
+
this.namespacedNativeEvents('click'),
|
220
|
+
function (e) {
|
221
|
+
e.preventDefault();
|
222
|
+
self.add(this);
|
223
|
+
});
|
224
|
+
|
225
|
+
// Bind remove links
|
226
|
+
// (Binded on document instead of container to not bypass click handler defined in jquery_ujs)
|
227
|
+
$(document).on(
|
228
|
+
this.namespacedNativeEvents('click'),
|
229
|
+
this.selector('remove', '#' + this.container.attr('id') + ' &'),
|
230
|
+
function (e) {
|
231
|
+
e.preventDefault();
|
232
|
+
self.remove(this);
|
233
|
+
});
|
234
|
+
|
235
|
+
// Bind options events
|
236
|
+
$.each(this.options, function (name, value) {
|
237
|
+
var bindMethod = 'bind' + name.charAt(0).toUpperCase() + name.slice(1);
|
238
|
+
if (value && self[bindMethod]) {
|
239
|
+
self[bindMethod]();
|
240
|
+
}
|
241
|
+
});
|
242
|
+
},
|
243
|
+
|
244
|
+
add: function (adder) {
|
245
|
+
var $adder = $(adder);
|
246
|
+
var insertionMethod = this.getInsertionMethod($adder);
|
247
|
+
var insertionNode = this.getInsertionNode($adder);
|
248
|
+
var contentTemplate = $adder.data('association-insertion-template');
|
249
|
+
var count = parseInt($adder.data('count'), 10) || 1;
|
250
|
+
|
251
|
+
for (var i = 0; i < count; i++) {
|
252
|
+
var contentNode = this.buildContentNode(contentTemplate);
|
253
|
+
var eventData = { link: $adder, node: contentNode, cocooned: this };
|
254
|
+
var afterNode = (insertionMethod === 'replaceWith' ? contentNode : insertionNode);
|
255
|
+
|
256
|
+
// Insertion can be prevented through a 'cocooned:before-insert' event handler
|
257
|
+
if (!this.notify(insertionNode, 'before-insert', eventData)) {
|
258
|
+
return false;
|
259
|
+
}
|
260
|
+
|
261
|
+
insertionNode[insertionMethod](contentNode);
|
262
|
+
|
263
|
+
this.notify(afterNode, 'after-insert', eventData);
|
264
|
+
}
|
265
|
+
},
|
266
|
+
|
267
|
+
remove: function (remover) {
|
268
|
+
var self = this;
|
269
|
+
var $remover = $(remover);
|
270
|
+
var nodeToDelete = this.findItem($remover);
|
271
|
+
var triggerNode = nodeToDelete.parent();
|
272
|
+
var eventData = { link: $remover, node: nodeToDelete, cocooned: this };
|
273
|
+
|
274
|
+
// Deletion can be prevented through a 'cocooned:before-remove' event handler
|
275
|
+
if (!this.notify(triggerNode, 'before-remove', eventData)) {
|
276
|
+
return false;
|
277
|
+
}
|
278
|
+
|
279
|
+
var timeout = triggerNode.data('remove-timeout') || 0;
|
280
|
+
|
281
|
+
setTimeout(function () {
|
282
|
+
if ($remover.hasClass('dynamic')) {
|
283
|
+
nodeToDelete.remove();
|
284
|
+
} else {
|
285
|
+
nodeToDelete.find('input[required], select[required]').each(function (index, element) {
|
286
|
+
$(element).removeAttr('required');
|
287
|
+
});
|
288
|
+
$remover.siblings('input[type=hidden][name$="[_destroy]"]').val('true');
|
289
|
+
nodeToDelete.hide();
|
290
|
+
}
|
291
|
+
self.notify(triggerNode, 'after-remove', eventData);
|
292
|
+
}, timeout);
|
293
|
+
},
|
294
|
+
|
295
|
+
hideMarkedForDestruction: function () {
|
296
|
+
var self = this;
|
297
|
+
$(this.selector('remove', '&.existing.destroyed'), this.container).each(function (i, removeLink) {
|
298
|
+
var node = self.findItem(removeLink);
|
299
|
+
node.hide();
|
300
|
+
});
|
301
|
+
}
|
302
|
+
};
|
303
|
+
|
304
|
+
Cocooned.Plugins.Limit = {
|
305
|
+
|
306
|
+
defaultOptionValue: false,
|
307
|
+
|
308
|
+
bindLimit: function () {
|
309
|
+
this.limit = this.options['limit'];
|
310
|
+
this.container.on('cocooned:before-insert', function (e) {
|
311
|
+
var cocooned = e.cocooned;
|
312
|
+
if (cocooned.getLength() < cocooned.limit) {
|
313
|
+
return;
|
314
|
+
}
|
315
|
+
|
316
|
+
e.stopPropagation();
|
317
|
+
var eventData = { link: e.link, node: e.node, cocooned: cocooned };
|
318
|
+
cocooned.notify(cocooned.container, 'limit-reached', eventData);
|
319
|
+
});
|
320
|
+
},
|
321
|
+
|
322
|
+
getLength: function () {
|
323
|
+
return this.getItems('&:visible').length;
|
324
|
+
}
|
325
|
+
};
|
326
|
+
|
327
|
+
Cocooned.Plugins.Reorderable = {
|
328
|
+
|
329
|
+
defaultOptionValue: true,
|
330
|
+
|
331
|
+
bindReorderable: function () {
|
332
|
+
var self = this;
|
333
|
+
|
334
|
+
// Maintain indexes
|
335
|
+
this.container
|
336
|
+
.on('cocooned:after-insert', function (e) { self.reindex(); })
|
337
|
+
.on('cocooned:after-remove', function (e) { self.reindex(); })
|
338
|
+
.on('cocooned:after-move', function (e) { self.reindex(); });
|
339
|
+
|
340
|
+
// Move items
|
341
|
+
this.container.on(
|
342
|
+
this.namespacedNativeEvents('click'),
|
343
|
+
[this.selector('up'), this.selector('down')].join(', '),
|
344
|
+
function (e) {
|
345
|
+
e.preventDefault();
|
346
|
+
var node = this;
|
347
|
+
var up = self.classes['up'].some(function (klass) {
|
348
|
+
return node.className.indexOf(klass) !== -1;
|
349
|
+
});
|
350
|
+
self.move(this, up ? 'up' : 'down');
|
351
|
+
});
|
352
|
+
|
353
|
+
// Ensure positions are unique before save
|
354
|
+
this.container.closest('form').on(
|
355
|
+
this.namespacedNativeEvents('submit'),
|
356
|
+
function (e) {
|
357
|
+
self.reindex();
|
358
|
+
});
|
359
|
+
},
|
360
|
+
|
361
|
+
move: function (moveLink, direction) {
|
362
|
+
var self = this;
|
363
|
+
var $mover = $(moveLink);
|
364
|
+
var node = $mover.closest(this.selector('item'));
|
365
|
+
var siblings = (direction === 'up'
|
366
|
+
? node.prevAll(this.selector('item', '&:eq(0)'))
|
367
|
+
: node.nextAll(this.selector('item', '&:eq(0)')));
|
368
|
+
|
369
|
+
if (siblings.length === 0) {
|
370
|
+
return;
|
371
|
+
}
|
372
|
+
|
373
|
+
// Move can be prevented through a 'cocooned:before-move' event handler
|
374
|
+
var eventData = { link: $mover, node: node, cocooned: this };
|
375
|
+
if (!self.notify(node, 'before-move', eventData)) {
|
376
|
+
return false;
|
377
|
+
}
|
378
|
+
|
379
|
+
var height = self.container.outerHeight();
|
380
|
+
var width = self.container.outerWidth();
|
381
|
+
|
382
|
+
self.container.css('height', height).css('width', width);
|
383
|
+
self.hide(node, function () {
|
384
|
+
var movedNode = $(this).detach();
|
385
|
+
movedNode[(direction === 'up' ? 'insertBefore' : 'insertAfter')](siblings);
|
386
|
+
|
387
|
+
self.show(movedNode, function () {
|
388
|
+
self.container.css('height', '').css('width', ''); // Object notation does not work here.
|
389
|
+
self.notify(movedNode, 'after-move', eventData);
|
390
|
+
});
|
391
|
+
});
|
392
|
+
},
|
393
|
+
|
394
|
+
reindex: function () {
|
395
|
+
var i = 0;
|
396
|
+
var nodes = this.getItems('&:visible');
|
397
|
+
var eventData = { link: null, nodes: nodes, cocooned: this };
|
398
|
+
|
399
|
+
// Reindex can be prevented through a 'cocooned:before-reindex' event handler
|
400
|
+
if (!this.notify(this.container, 'before-reindex', eventData)) {
|
401
|
+
return false;
|
402
|
+
}
|
403
|
+
|
404
|
+
nodes.each(function () { $('input[id$=_position]', this).val(++i); });
|
405
|
+
this.notify(this.container, 'after-reindex', eventData);
|
406
|
+
},
|
407
|
+
|
408
|
+
show: function (node, callback) {
|
409
|
+
callback = callback || function () { return true; };
|
410
|
+
|
411
|
+
node.addClass('cocooned-visible-item');
|
412
|
+
setTimeout(function () {
|
413
|
+
callback.apply(node);
|
414
|
+
node.removeClass('cocooned-hidden-item');
|
415
|
+
}, 500);
|
416
|
+
},
|
417
|
+
|
418
|
+
hide: function (node, callback) {
|
419
|
+
node.removeClass('cocooned-visible-item').addClass('cocooned-hidden-item');
|
420
|
+
if (callback) {
|
421
|
+
setTimeout(function () {
|
422
|
+
callback.apply(node);
|
423
|
+
}, 500);
|
424
|
+
}
|
425
|
+
}
|
426
|
+
};
|
427
|
+
|
428
|
+
// Expose a jQuery plugin
|
429
|
+
$.fn.cocooned = function (options) {
|
430
|
+
return this.each(function () {
|
431
|
+
var container = $(this);
|
432
|
+
if (typeof container.data('cocooned') !== 'undefined') {
|
433
|
+
return;
|
434
|
+
}
|
435
|
+
|
436
|
+
var cocooned = new Cocooned(container, options);
|
437
|
+
container.data('cocooned', cocooned);
|
438
|
+
});
|
439
|
+
};
|
440
|
+
|
441
|
+
// On-load initialization
|
442
|
+
$(function () {
|
443
|
+
$('*[data-cocooned-options]').each(function (i, el) {
|
444
|
+
$(el).cocooned($(el).data('cocooned-options'));
|
445
|
+
});
|
446
|
+
});
|
447
|
+
|
448
|
+
return Cocooned;
|
449
|
+
}));
|