decidim-admin 0.0.1.alpha9 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of decidim-admin might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +33 -8
- data/Rakefile +0 -25
- data/app/assets/javascripts/decidim/admin/application.js.es6 +17 -10
- data/app/assets/javascripts/decidim/admin/sort_steps.js.es6 +20 -11
- data/app/assets/javascripts/decidim/admin/tab_focus.js.es6 +22 -0
- data/app/assets/stylesheets/decidim/admin/_forms.scss +10 -0
- data/app/assets/stylesheets/decidim/admin/_layout.scss +11 -0
- data/app/assets/stylesheets/decidim/admin/_tables.scss +4 -0
- data/app/assets/stylesheets/decidim/admin/application.scss +8 -2
- data/app/commands/decidim/admin/activate_participatory_process_step.rb +1 -1
- data/app/commands/decidim/admin/create_category.rb +44 -0
- data/app/commands/decidim/admin/create_feature.rb +48 -0
- data/app/commands/decidim/admin/create_participatory_process.rb +3 -6
- data/app/commands/decidim/admin/create_participatory_process_admin.rb +51 -0
- data/app/commands/decidim/admin/create_participatory_process_attachment.rb +44 -0
- data/app/commands/decidim/admin/create_participatory_process_step.rb +3 -3
- data/app/commands/decidim/admin/create_scope.rb +38 -0
- data/app/commands/decidim/admin/create_static_page.rb +40 -0
- data/app/commands/decidim/admin/destroy_category.rb +36 -0
- data/app/commands/decidim/admin/destroy_feature.rb +39 -0
- data/app/commands/decidim/admin/publish_participatory_process.rb +1 -1
- data/app/commands/decidim/admin/reorder_participatory_process_steps.rb +5 -2
- data/app/commands/decidim/admin/unpublish_participatory_process.rb +1 -1
- data/app/commands/decidim/admin/update_category.rb +48 -0
- data/app/commands/decidim/admin/update_organization.rb +49 -0
- data/app/commands/decidim/admin/update_participatory_process.rb +2 -2
- data/app/commands/decidim/admin/update_participatory_process_attachment.rb +49 -0
- data/app/commands/decidim/admin/update_participatory_process_step.rb +1 -1
- data/app/commands/decidim/admin/update_scope.rb +43 -0
- data/app/commands/decidim/admin/update_static_page.rb +45 -0
- data/app/constraints/decidim/admin/organization_dashboard_constraint.rb +5 -1
- data/app/controllers/decidim/admin/application_controller.rb +8 -0
- data/app/controllers/decidim/admin/categories_controller.rb +90 -0
- data/app/controllers/decidim/admin/concerns/participatory_process_admin.rb +31 -0
- data/app/controllers/decidim/admin/features_controller.rb +74 -0
- data/app/controllers/decidim/admin/organization_controller.rb +40 -0
- data/app/controllers/decidim/admin/participatory_process_attachments_controller.rb +84 -0
- data/app/controllers/decidim/admin/participatory_process_publications_controller.rb +2 -7
- data/app/controllers/decidim/admin/participatory_process_step_activations_controller.rb +6 -8
- data/app/controllers/decidim/admin/participatory_process_step_ordering_controller.rb +2 -4
- data/app/controllers/decidim/admin/participatory_process_steps_controller.rb +13 -11
- data/app/controllers/decidim/admin/participatory_process_user_roles_controller.rb +54 -0
- data/app/controllers/decidim/admin/participatory_processes_controller.rb +11 -6
- data/app/controllers/decidim/admin/scopes_controller.rb +79 -0
- data/app/controllers/decidim/admin/static_pages_controller.rb +94 -0
- data/app/forms/decidim/admin/category_form.rb +37 -0
- data/app/forms/decidim/admin/feature_form.rb +16 -0
- data/app/forms/decidim/admin/organization_form.rb +31 -0
- data/app/forms/decidim/admin/participatory_process_attachment_form.rb +19 -0
- data/app/forms/decidim/admin/participatory_process_form.rb +4 -7
- data/app/forms/decidim/admin/participatory_process_step_form.rb +12 -2
- data/app/forms/decidim/admin/participatory_process_user_role_form.rb +15 -0
- data/app/forms/decidim/admin/scope_form.rb +24 -0
- data/app/forms/decidim/admin/static_page_form.rb +30 -0
- data/app/helpers/decidim/admin/application_helper.rb +1 -0
- data/app/helpers/decidim/admin/aria_selected_link_to_helper.rb +28 -0
- data/app/helpers/decidim/admin/attributes_display_helper.rb +13 -5
- data/app/models/decidim/admin/abilities/admin_user.rb +34 -0
- data/app/models/decidim/admin/abilities/base.rb +19 -0
- data/app/models/decidim/admin/abilities/participatory_process_admin.rb +51 -0
- data/app/models/decidim/admin/participatory_process_user_role.rb +14 -0
- data/app/queries/decidim/admin/manageable_participatory_processes_for_user.rb +41 -0
- data/app/queries/decidim/admin/process_admin_roles_for_process.rb +35 -0
- data/app/views/decidim/admin/categories/_form.html.erb +12 -0
- data/app/views/decidim/admin/categories/edit.html.erb +9 -0
- data/app/views/decidim/admin/categories/index.html.erb +44 -0
- data/app/views/decidim/admin/categories/new.html.erb +9 -0
- data/app/views/decidim/admin/categories/show.html.erb +14 -0
- data/app/views/decidim/admin/features/_feature.html.erb +20 -0
- data/app/views/decidim/admin/features/_form.html.erb +3 -0
- data/app/views/decidim/admin/features/index.html.erb +23 -0
- data/app/views/decidim/admin/features/new.html.erb +9 -0
- data/app/views/decidim/admin/organization/_form.html.erb +23 -0
- data/app/views/decidim/admin/organization/edit.html.erb +11 -0
- data/app/views/decidim/admin/participatory_process_attachments/_form.html.erb +11 -0
- data/app/views/decidim/admin/participatory_process_attachments/edit.html.erb +9 -0
- data/app/views/decidim/admin/participatory_process_attachments/index.html.erb +37 -0
- data/app/views/decidim/admin/participatory_process_attachments/new.html.erb +9 -0
- data/app/views/decidim/admin/participatory_process_attachments/show.html.erb +25 -0
- data/app/views/decidim/admin/participatory_process_steps/_form.html.erb +2 -2
- data/app/views/decidim/admin/participatory_process_steps/edit.html.erb +1 -3
- data/app/views/decidim/admin/participatory_process_steps/{_table.html.erb → index.html.erb} +15 -11
- data/app/views/decidim/admin/participatory_process_steps/new.html.erb +1 -3
- data/app/views/decidim/admin/participatory_process_steps/show.html.erb +1 -4
- data/app/views/decidim/admin/participatory_process_user_roles/index.html.erb +34 -0
- data/app/views/decidim/admin/participatory_processes/_form.html.erb +2 -2
- data/app/views/decidim/admin/participatory_processes/edit.html.erb +13 -3
- data/app/views/decidim/admin/participatory_processes/index.html.erb +5 -14
- data/app/views/decidim/admin/participatory_processes/show.html.erb +19 -36
- data/app/views/decidim/admin/scopes/_form.html.erb +3 -0
- data/app/views/decidim/admin/scopes/edit.html.erb +11 -0
- data/app/views/decidim/admin/scopes/index.html.erb +38 -0
- data/app/views/decidim/admin/scopes/new.html.erb +11 -0
- data/app/views/decidim/admin/static_pages/_form.html.erb +13 -0
- data/app/views/decidim/admin/static_pages/edit.html.erb +11 -0
- data/app/views/decidim/admin/static_pages/index.html.erb +40 -0
- data/app/views/decidim/admin/static_pages/new.html.erb +11 -0
- data/app/views/decidim/admin/static_pages/show.html.erb +22 -0
- data/app/views/layouts/decidim/admin/_application.html.erb +40 -0
- data/app/views/layouts/decidim/admin/_sidebar.html.erb +5 -2
- data/app/views/layouts/decidim/admin/application.html.erb +3 -40
- data/app/views/layouts/decidim/admin/participatory_process.html.erb +54 -0
- data/config/i18n-tasks.yml +3 -2
- data/config/locales/ca.yml +138 -6
- data/config/locales/en.yml +191 -13
- data/config/locales/es.yml +139 -7
- data/config/routes.rb +23 -1
- data/db/migrate/20161102144648_add_admin_participatory_process_user_roles.rb +15 -0
- data/db/seeds.rb +21 -0
- data/lib/decidim/admin/engine.rb +10 -3
- data/lib/decidim/admin/features/base_controller.rb +33 -0
- data/lib/decidim/admin/features.rb +10 -0
- data/lib/decidim/admin.rb +1 -0
- data/vendor/assets/javascripts/html.sortable.js +691 -0
- metadata +98 -33
- data/LICENSE.txt +0 -619
- data/app/models/decidim/admin/abilities/admin.rb +0 -21
- data/vendor/assets/javascripts/html.sortable.min.js +0 -2
@@ -0,0 +1,691 @@
|
|
1
|
+
;(function(root, factory) {
|
2
|
+
if (typeof define === 'function' && define.amd) {
|
3
|
+
define([], factory);
|
4
|
+
} else if (typeof exports === 'object') {
|
5
|
+
module.exports = factory();
|
6
|
+
} else {
|
7
|
+
root.sortable = factory();
|
8
|
+
}
|
9
|
+
}(this, function() {
|
10
|
+
/*
|
11
|
+
* HTML5 Sortable library
|
12
|
+
* https://github.com/voidberg/html5sortable
|
13
|
+
*
|
14
|
+
* Original code copyright 2012 Ali Farhadi.
|
15
|
+
* This version is mantained by Alexandru Badiu <andu@ctrlz.ro> & Lukas Oppermann <lukas@vea.re>
|
16
|
+
* jQuery-independent implementation by Nazar Mokrynskyi <nazar@mokrynskyi.com>
|
17
|
+
*
|
18
|
+
* Released under the MIT license.
|
19
|
+
*/
|
20
|
+
'use strict';
|
21
|
+
/*
|
22
|
+
* variables global to the plugin
|
23
|
+
*/
|
24
|
+
var dragging;
|
25
|
+
var draggingHeight;
|
26
|
+
var placeholders = [];
|
27
|
+
var sortables = [];
|
28
|
+
/**
|
29
|
+
* Get or set data on element
|
30
|
+
* @param {Element} element
|
31
|
+
* @param {string} key
|
32
|
+
* @param {*} value
|
33
|
+
* @return {*}
|
34
|
+
*/
|
35
|
+
var _data = function(element, key, value) {
|
36
|
+
if (value === undefined) {
|
37
|
+
return element && element.h5s && element.h5s.data && element.h5s.data[key];
|
38
|
+
} else {
|
39
|
+
element.h5s = element.h5s || {};
|
40
|
+
element.h5s.data = element.h5s.data || {};
|
41
|
+
element.h5s.data[key] = value;
|
42
|
+
}
|
43
|
+
};
|
44
|
+
/**
|
45
|
+
* Remove data from element
|
46
|
+
* @param {Element} element
|
47
|
+
*/
|
48
|
+
var _removeData = function(element) {
|
49
|
+
if (element.h5s) {
|
50
|
+
delete element.h5s.data;
|
51
|
+
}
|
52
|
+
};
|
53
|
+
/**
|
54
|
+
* Cross-browser shortcut for actual `Element.matches` method,
|
55
|
+
* which has vendor prefix in older browsers
|
56
|
+
*/
|
57
|
+
var matches;
|
58
|
+
switch (true) {
|
59
|
+
case 'matches' in window.Element.prototype:
|
60
|
+
matches = 'matches';
|
61
|
+
break;
|
62
|
+
case 'mozMatchesSelector' in window.Element.prototype:
|
63
|
+
matches = 'mozMatchesSelector';
|
64
|
+
break;
|
65
|
+
case 'msMatchesSelector' in window.Element.prototype:
|
66
|
+
matches = 'msMatchesSelector';
|
67
|
+
break;
|
68
|
+
case 'webkitMatchesSelector' in window.Element.prototype:
|
69
|
+
matches = 'webkitMatchesSelector';
|
70
|
+
break;
|
71
|
+
}
|
72
|
+
/**
|
73
|
+
* Filter only wanted nodes
|
74
|
+
* @param {Array|NodeList} nodes
|
75
|
+
* @param {Array/string} wanted
|
76
|
+
* @returns {Array}
|
77
|
+
*/
|
78
|
+
var _filter = function(nodes, wanted) {
|
79
|
+
if (!wanted) {
|
80
|
+
return Array.prototype.slice.call(nodes);
|
81
|
+
}
|
82
|
+
var result = [];
|
83
|
+
for (var i = 0; i < nodes.length; ++i) {
|
84
|
+
if (typeof wanted === 'string' && nodes[i][matches](wanted)) {
|
85
|
+
result.push(nodes[i]);
|
86
|
+
}
|
87
|
+
if (wanted.indexOf(nodes[i]) !== -1) {
|
88
|
+
result.push(nodes[i]);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
return result;
|
92
|
+
};
|
93
|
+
/**
|
94
|
+
* @param {Array|Element} element
|
95
|
+
* @param {Array|string} event
|
96
|
+
* @param {Function} callback
|
97
|
+
*/
|
98
|
+
var _on = function(element, event, callback) {
|
99
|
+
if (element instanceof Array) {
|
100
|
+
for (var i = 0; i < element.length; ++i) {
|
101
|
+
_on(element[i], event, callback);
|
102
|
+
}
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
element.addEventListener(event, callback);
|
106
|
+
element.h5s = element.h5s || {};
|
107
|
+
element.h5s.events = element.h5s.events || {};
|
108
|
+
element.h5s.events[event] = callback;
|
109
|
+
};
|
110
|
+
/**
|
111
|
+
* @param {Array|Element} element
|
112
|
+
* @param {Array|string} event
|
113
|
+
*/
|
114
|
+
var _off = function(element, event) {
|
115
|
+
if (element instanceof Array) {
|
116
|
+
for (var i = 0; i < element.length; ++i) {
|
117
|
+
_off(element[i], event);
|
118
|
+
}
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
if (element.h5s && element.h5s.events && element.h5s.events[event]) {
|
122
|
+
element.removeEventListener(event, element.h5s.events[event]);
|
123
|
+
delete element.h5s.events[event];
|
124
|
+
}
|
125
|
+
};
|
126
|
+
/**
|
127
|
+
* @param {Array|Element} element
|
128
|
+
* @param {string} attribute
|
129
|
+
* @param {*} value
|
130
|
+
*/
|
131
|
+
var _attr = function(element, attribute, value) {
|
132
|
+
if (element instanceof Array) {
|
133
|
+
for (var i = 0; i < element.length; ++i) {
|
134
|
+
_attr(element[i], attribute, value);
|
135
|
+
}
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
element.setAttribute(attribute, value);
|
139
|
+
};
|
140
|
+
/**
|
141
|
+
* @param {Array|Element} element
|
142
|
+
* @param {string} attribute
|
143
|
+
*/
|
144
|
+
var _removeAttr = function(element, attribute) {
|
145
|
+
if (element instanceof Array) {
|
146
|
+
for (var i = 0; i < element.length; ++i) {
|
147
|
+
_removeAttr(element[i], attribute);
|
148
|
+
}
|
149
|
+
return;
|
150
|
+
}
|
151
|
+
element.removeAttribute(attribute);
|
152
|
+
};
|
153
|
+
/**
|
154
|
+
* @param {Element} element
|
155
|
+
* @returns {{left: *, top: *}}
|
156
|
+
*/
|
157
|
+
var _offset = function(element) {
|
158
|
+
var rect = element.getClientRects()[0];
|
159
|
+
return {
|
160
|
+
left: rect.left + window.scrollX,
|
161
|
+
top: rect.top + window.scrollY
|
162
|
+
};
|
163
|
+
};
|
164
|
+
/*
|
165
|
+
* remove event handlers from items
|
166
|
+
* @param {Array|NodeList} items
|
167
|
+
*/
|
168
|
+
var _removeItemEvents = function(items) {
|
169
|
+
_off(items, 'dragstart');
|
170
|
+
_off(items, 'dragend');
|
171
|
+
_off(items, 'selectstart');
|
172
|
+
_off(items, 'dragover');
|
173
|
+
_off(items, 'dragenter');
|
174
|
+
_off(items, 'drop');
|
175
|
+
};
|
176
|
+
/*
|
177
|
+
* Remove event handlers from sortable
|
178
|
+
* @param {Element} sortable a single sortable
|
179
|
+
*/
|
180
|
+
var _removeSortableEvents = function(sortable) {
|
181
|
+
_off(sortable, 'dragover');
|
182
|
+
_off(sortable, 'dragenter');
|
183
|
+
_off(sortable, 'drop');
|
184
|
+
};
|
185
|
+
/*
|
186
|
+
* Attach ghost to dataTransfer object
|
187
|
+
* @param {Event} original event
|
188
|
+
* @param {object} ghost-object with item, x and y coordinates
|
189
|
+
*/
|
190
|
+
var _attachGhost = function(event, ghost) {
|
191
|
+
// this needs to be set for HTML5 drag & drop to work
|
192
|
+
event.dataTransfer.effectAllowed = 'move';
|
193
|
+
event.dataTransfer.setData('text', '');
|
194
|
+
|
195
|
+
// check if setDragImage method is available
|
196
|
+
if (event.dataTransfer.setDragImage) {
|
197
|
+
event.dataTransfer.setDragImage(ghost.draggedItem, ghost.x, ghost.y);
|
198
|
+
}
|
199
|
+
};
|
200
|
+
/**
|
201
|
+
* _addGhostPos clones the dragged item and adds it as a Ghost item
|
202
|
+
* @param {Event} event - the event fired when dragstart is triggered
|
203
|
+
* @param {object} ghost - .draggedItem = Element
|
204
|
+
*/
|
205
|
+
var _addGhostPos = function(event, ghost) {
|
206
|
+
if (!ghost.x) {
|
207
|
+
ghost.x = parseInt(event.pageX - _offset(ghost.draggedItem).left);
|
208
|
+
}
|
209
|
+
if (!ghost.y) {
|
210
|
+
ghost.y = parseInt(event.pageY - _offset(ghost.draggedItem).top);
|
211
|
+
}
|
212
|
+
return ghost;
|
213
|
+
};
|
214
|
+
/**
|
215
|
+
* _makeGhost decides which way to make a ghost and passes it to attachGhost
|
216
|
+
* @param {Element} draggedItem - the item that the user drags
|
217
|
+
*/
|
218
|
+
var _makeGhost = function(draggedItem) {
|
219
|
+
return {
|
220
|
+
draggedItem: draggedItem
|
221
|
+
};
|
222
|
+
};
|
223
|
+
/**
|
224
|
+
* _getGhost constructs ghost and attaches it to dataTransfer
|
225
|
+
* @param {Event} event - the original drag event object
|
226
|
+
* @param {Element} draggedItem - the item that the user drags
|
227
|
+
*/
|
228
|
+
// TODO: could draggedItem be replaced by event.target in all instances
|
229
|
+
var _getGhost = function(event, draggedItem) {
|
230
|
+
// add ghost item & draggedItem to ghost object
|
231
|
+
var ghost = _makeGhost(draggedItem);
|
232
|
+
// attach ghost position
|
233
|
+
ghost = _addGhostPos(event, ghost);
|
234
|
+
// attach ghost to dataTransfer
|
235
|
+
_attachGhost(event, ghost);
|
236
|
+
};
|
237
|
+
/*
|
238
|
+
* Remove data from sortable
|
239
|
+
* @param {Element} sortable a single sortable
|
240
|
+
*/
|
241
|
+
var _removeSortableData = function(sortable) {
|
242
|
+
_removeData(sortable);
|
243
|
+
_removeAttr(sortable, 'aria-dropeffect');
|
244
|
+
};
|
245
|
+
/*
|
246
|
+
* Remove data from items
|
247
|
+
* @param {Array|Element} items
|
248
|
+
*/
|
249
|
+
var _removeItemData = function(items) {
|
250
|
+
_removeAttr(items, 'aria-grabbed');
|
251
|
+
_removeAttr(items, 'draggable');
|
252
|
+
_removeAttr(items, 'role');
|
253
|
+
};
|
254
|
+
/*
|
255
|
+
* Check if two lists are connected
|
256
|
+
* @param {Element} curList
|
257
|
+
* @param {Element} destList
|
258
|
+
*/
|
259
|
+
var _listsConnected = function(curList, destList) {
|
260
|
+
if (curList === destList) {
|
261
|
+
return true;
|
262
|
+
}
|
263
|
+
if (_data(curList, 'connectWith') !== undefined) {
|
264
|
+
return _data(curList, 'connectWith') === _data(destList, 'connectWith');
|
265
|
+
}
|
266
|
+
return false;
|
267
|
+
};
|
268
|
+
var _getHandles = function(items, handle) {
|
269
|
+
var result = [];
|
270
|
+
var handles;
|
271
|
+
if (!handle) {
|
272
|
+
return items;
|
273
|
+
}
|
274
|
+
for (var i = 0; i < items.length; ++i) {
|
275
|
+
handles = items[i].querySelectorAll(handle);
|
276
|
+
result = result.concat(Array.prototype.slice.call(handles));
|
277
|
+
}
|
278
|
+
return result;
|
279
|
+
};
|
280
|
+
/*
|
281
|
+
* Destroy the sortable
|
282
|
+
* @param {Element} sortableElement a single sortable
|
283
|
+
*/
|
284
|
+
var _destroySortable = function(sortableElement) {
|
285
|
+
var opts = _data(sortableElement, 'opts') || {};
|
286
|
+
var items = _filter(sortableElement.children, opts.items);
|
287
|
+
var handles = _getHandles(items, opts.handle);
|
288
|
+
// remove event handlers & data from sortable
|
289
|
+
_removeSortableEvents(sortableElement);
|
290
|
+
_removeSortableData(sortableElement);
|
291
|
+
// remove event handlers & data from items
|
292
|
+
_off(handles, 'mousedown');
|
293
|
+
_removeItemEvents(items);
|
294
|
+
_removeItemData(items);
|
295
|
+
};
|
296
|
+
/*
|
297
|
+
* Enable the sortable
|
298
|
+
* @param {Element} sortableElement a single sortable
|
299
|
+
*/
|
300
|
+
var _enableSortable = function(sortableElement) {
|
301
|
+
var opts = _data(sortableElement, 'opts');
|
302
|
+
var items = _filter(sortableElement.children, opts.items);
|
303
|
+
var handles = _getHandles(items, opts.handle);
|
304
|
+
_attr(sortableElement, 'aria-dropeffect', 'move');
|
305
|
+
_attr(handles, 'draggable', 'true');
|
306
|
+
// IE FIX for ghost
|
307
|
+
// can be disabled as it has the side effect that other events
|
308
|
+
// (e.g. click) will be ignored
|
309
|
+
var spanEl = (document || window.document).createElement('span');
|
310
|
+
if (typeof spanEl.dragDrop === 'function' && !opts.disableIEFix) {
|
311
|
+
_on(handles, 'mousedown', function() {
|
312
|
+
if (items.indexOf(this) !== -1) {
|
313
|
+
this.dragDrop();
|
314
|
+
} else {
|
315
|
+
var parent = this.parentElement;
|
316
|
+
while (items.indexOf(parent) === -1) {
|
317
|
+
parent = parent.parentElement;
|
318
|
+
}
|
319
|
+
parent.dragDrop();
|
320
|
+
}
|
321
|
+
});
|
322
|
+
}
|
323
|
+
};
|
324
|
+
/*
|
325
|
+
* Disable the sortable
|
326
|
+
* @param {Element} sortableElement a single sortable
|
327
|
+
*/
|
328
|
+
var _disableSortable = function(sortableElement) {
|
329
|
+
var opts = _data(sortableElement, 'opts');
|
330
|
+
var items = _filter(sortableElement.children, opts.items);
|
331
|
+
var handles = _getHandles(items, opts.handle);
|
332
|
+
_attr(sortableElement, 'aria-dropeffect', 'none');
|
333
|
+
_attr(handles, 'draggable', 'false');
|
334
|
+
_off(handles, 'mousedown');
|
335
|
+
};
|
336
|
+
/*
|
337
|
+
* Reload the sortable
|
338
|
+
* @param {Element} sortableElement a single sortable
|
339
|
+
* @description events need to be removed to not be double bound
|
340
|
+
*/
|
341
|
+
var _reloadSortable = function(sortableElement) {
|
342
|
+
var opts = _data(sortableElement, 'opts');
|
343
|
+
var items = _filter(sortableElement.children, opts.items);
|
344
|
+
var handles = _getHandles(items, opts.handle);
|
345
|
+
// remove event handlers from items
|
346
|
+
_removeItemEvents(items);
|
347
|
+
_off(handles, 'mousedown');
|
348
|
+
// remove event handlers from sortable
|
349
|
+
_removeSortableEvents(sortableElement);
|
350
|
+
};
|
351
|
+
/**
|
352
|
+
* Get position of the element relatively to its sibling elements
|
353
|
+
* @param {Element} element
|
354
|
+
* @returns {number}
|
355
|
+
*/
|
356
|
+
var _index = function(element) {
|
357
|
+
if (!element.parentElement) {
|
358
|
+
return 0;
|
359
|
+
}
|
360
|
+
return Array.prototype.indexOf.call(element.parentElement.children, element);
|
361
|
+
};
|
362
|
+
/**
|
363
|
+
* Whether element is in DOM
|
364
|
+
* @param {Element} element
|
365
|
+
* @returns {boolean}
|
366
|
+
*/
|
367
|
+
var _attached = function(element) {
|
368
|
+
return !!element.parentNode;
|
369
|
+
};
|
370
|
+
/**
|
371
|
+
* Convert HTML string into DOM element
|
372
|
+
* @param {Element|string} html
|
373
|
+
* @returns {Element}
|
374
|
+
*/
|
375
|
+
var _html2element = function(html) {
|
376
|
+
if (typeof html !== 'string') {
|
377
|
+
return html;
|
378
|
+
}
|
379
|
+
var div = document.createElement('div');
|
380
|
+
div.innerHTML = html;
|
381
|
+
return div.firstChild;
|
382
|
+
};
|
383
|
+
/**
|
384
|
+
* Insert before target
|
385
|
+
* @param {Element} target
|
386
|
+
* @param {Element} element
|
387
|
+
*/
|
388
|
+
var _before = function(target, element) {
|
389
|
+
target.parentElement.insertBefore(
|
390
|
+
element,
|
391
|
+
target
|
392
|
+
);
|
393
|
+
};
|
394
|
+
/**
|
395
|
+
* Insert after target
|
396
|
+
* @param {Element} target
|
397
|
+
* @param {Element} element
|
398
|
+
*/
|
399
|
+
var _after = function(target, element) {
|
400
|
+
target.parentElement.insertBefore(
|
401
|
+
element,
|
402
|
+
target.nextElementSibling
|
403
|
+
);
|
404
|
+
};
|
405
|
+
/**
|
406
|
+
* Detach element from DOM
|
407
|
+
* @param {Element} element
|
408
|
+
*/
|
409
|
+
var _detach = function(element) {
|
410
|
+
if (element.parentNode) {
|
411
|
+
element.parentNode.removeChild(element);
|
412
|
+
}
|
413
|
+
};
|
414
|
+
/**
|
415
|
+
* Make native event that can be dispatched afterwards
|
416
|
+
* @param {string} name
|
417
|
+
* @param {object} detail
|
418
|
+
* @returns {CustomEvent}
|
419
|
+
*/
|
420
|
+
var _makeEvent = function(name, detail) {
|
421
|
+
var e = document.createEvent('Event');
|
422
|
+
if (detail) {
|
423
|
+
e.detail = detail;
|
424
|
+
}
|
425
|
+
e.initEvent(name, false, true);
|
426
|
+
return e;
|
427
|
+
};
|
428
|
+
/**
|
429
|
+
* @param {Element} sortableElement
|
430
|
+
* @param {CustomEvent} event
|
431
|
+
*/
|
432
|
+
var _dispatchEventOnConnected = function(sortableElement, event) {
|
433
|
+
sortables.forEach(function(target) {
|
434
|
+
if (_listsConnected(sortableElement, target)) {
|
435
|
+
target.dispatchEvent(event);
|
436
|
+
}
|
437
|
+
});
|
438
|
+
};
|
439
|
+
/*
|
440
|
+
* Public sortable object
|
441
|
+
* @param {Array|NodeList} sortableElements
|
442
|
+
* @param {object|string} options|method
|
443
|
+
*/
|
444
|
+
var sortable = function(sortableElements, options) {
|
445
|
+
|
446
|
+
var method = String(options);
|
447
|
+
|
448
|
+
options = (function(options) {
|
449
|
+
var result = {
|
450
|
+
connectWith: false,
|
451
|
+
placeholder: null,
|
452
|
+
// dragImage can be null or a Element
|
453
|
+
dragImage: null,
|
454
|
+
disableIEFix: false,
|
455
|
+
placeholderClass: 'sortable-placeholder',
|
456
|
+
draggingClass: 'sortable-dragging',
|
457
|
+
hoverClass: false
|
458
|
+
};
|
459
|
+
for (var option in options) {
|
460
|
+
result[option] = options[option];
|
461
|
+
}
|
462
|
+
return result;
|
463
|
+
})(options);
|
464
|
+
|
465
|
+
if (typeof sortableElements === 'string') {
|
466
|
+
sortableElements = document.querySelectorAll(sortableElements);
|
467
|
+
}
|
468
|
+
|
469
|
+
if (sortableElements instanceof window.Element) {
|
470
|
+
sortableElements = [sortableElements];
|
471
|
+
}
|
472
|
+
|
473
|
+
sortableElements = Array.prototype.slice.call(sortableElements);
|
474
|
+
|
475
|
+
/* TODO: maxstatements should be 25, fix and remove line below */
|
476
|
+
/*jshint maxstatements:false */
|
477
|
+
sortableElements.forEach(function(sortableElement) {
|
478
|
+
|
479
|
+
if (/enable|disable|destroy/.test(method)) {
|
480
|
+
sortable[method](sortableElement);
|
481
|
+
return;
|
482
|
+
}
|
483
|
+
|
484
|
+
// get options & set options on sortable
|
485
|
+
options = _data(sortableElement, 'opts') || options;
|
486
|
+
_data(sortableElement, 'opts', options);
|
487
|
+
// reset sortable
|
488
|
+
_reloadSortable(sortableElement);
|
489
|
+
// initialize
|
490
|
+
var items = _filter(sortableElement.children, options.items);
|
491
|
+
var index;
|
492
|
+
var startParent;
|
493
|
+
var placeholder = options.placeholder;
|
494
|
+
if (!placeholder) {
|
495
|
+
placeholder = document.createElement(
|
496
|
+
/^ul|ol$/i.test(sortableElement.tagName) ? 'li' : 'div'
|
497
|
+
);
|
498
|
+
}
|
499
|
+
placeholder = _html2element(placeholder);
|
500
|
+
placeholder.classList.add.apply(
|
501
|
+
placeholder.classList,
|
502
|
+
options.placeholderClass.split(' ')
|
503
|
+
);
|
504
|
+
|
505
|
+
// setup sortable ids
|
506
|
+
if (!sortableElement.getAttribute('data-sortable-id')) {
|
507
|
+
var id = sortables.length;
|
508
|
+
sortables[id] = sortableElement;
|
509
|
+
_attr(sortableElement, 'data-sortable-id', id);
|
510
|
+
_attr(items, 'data-item-sortable-id', id);
|
511
|
+
}
|
512
|
+
|
513
|
+
_data(sortableElement, 'items', options.items);
|
514
|
+
placeholders.push(placeholder);
|
515
|
+
if (options.connectWith) {
|
516
|
+
_data(sortableElement, 'connectWith', options.connectWith);
|
517
|
+
}
|
518
|
+
|
519
|
+
_enableSortable(sortableElement);
|
520
|
+
_attr(items, 'role', 'option');
|
521
|
+
_attr(items, 'aria-grabbed', 'false');
|
522
|
+
|
523
|
+
// Mouse over class
|
524
|
+
if (options.hoverClass) {
|
525
|
+
var hoverClass = 'sortable-over';
|
526
|
+
if (typeof options.hoverClass === 'string') {
|
527
|
+
hoverClass = options.hoverClass;
|
528
|
+
}
|
529
|
+
|
530
|
+
_on(items, 'mouseenter', function() {
|
531
|
+
this.classList.add(hoverClass);
|
532
|
+
});
|
533
|
+
_on(items, 'mouseleave', function() {
|
534
|
+
this.classList.remove(hoverClass);
|
535
|
+
});
|
536
|
+
}
|
537
|
+
|
538
|
+
// Handle drag events on draggable items
|
539
|
+
_on(items, 'dragstart', function(e) {
|
540
|
+
e.stopImmediatePropagation();
|
541
|
+
|
542
|
+
if (options.dragImage) {
|
543
|
+
_attachGhost(e, {
|
544
|
+
draggedItem: options.dragImage,
|
545
|
+
x: 0,
|
546
|
+
y: 0
|
547
|
+
});
|
548
|
+
console.log('WARNING: dragImage option is deprecated' +
|
549
|
+
' and will be removed in the future!');
|
550
|
+
} else {
|
551
|
+
// add transparent clone or other ghost to cursor
|
552
|
+
_getGhost(e, this);
|
553
|
+
}
|
554
|
+
// cache selsection & add attr for dragging
|
555
|
+
this.classList.add(options.draggingClass);
|
556
|
+
dragging = this;
|
557
|
+
_attr(dragging, 'aria-grabbed', 'true');
|
558
|
+
// grab values
|
559
|
+
index = _index(dragging);
|
560
|
+
draggingHeight = parseInt(window.getComputedStyle(dragging).height);
|
561
|
+
startParent = this.parentElement;
|
562
|
+
// dispatch sortstart event on each element in group
|
563
|
+
_dispatchEventOnConnected(sortableElement, _makeEvent('sortstart', {
|
564
|
+
item: dragging,
|
565
|
+
placeholder: placeholder,
|
566
|
+
startparent: startParent
|
567
|
+
}));
|
568
|
+
});
|
569
|
+
// Handle drag events on draggable items
|
570
|
+
_on(items, 'dragend', function() {
|
571
|
+
var newParent;
|
572
|
+
if (!dragging) {
|
573
|
+
return;
|
574
|
+
}
|
575
|
+
// remove dragging attributes and show item
|
576
|
+
dragging.classList.remove(options.draggingClass);
|
577
|
+
_attr(dragging, 'aria-grabbed', 'false');
|
578
|
+
dragging.style.display = dragging.oldDisplay;
|
579
|
+
delete dragging.oldDisplay;
|
580
|
+
|
581
|
+
placeholders.forEach(_detach);
|
582
|
+
newParent = this.parentElement;
|
583
|
+
_dispatchEventOnConnected(sortableElement, _makeEvent('sortstop', {
|
584
|
+
item: dragging,
|
585
|
+
startparent: startParent
|
586
|
+
}));
|
587
|
+
if (index !== _index(dragging) || startParent !== newParent) {
|
588
|
+
_dispatchEventOnConnected(sortableElement, _makeEvent('sortupdate', {
|
589
|
+
item: dragging,
|
590
|
+
index: _filter(newParent.children, _data(newParent, 'items'))
|
591
|
+
.indexOf(dragging),
|
592
|
+
oldindex: items.indexOf(dragging),
|
593
|
+
elementIndex: _index(dragging),
|
594
|
+
oldElementIndex: index,
|
595
|
+
startparent: startParent,
|
596
|
+
endparent: newParent
|
597
|
+
}));
|
598
|
+
}
|
599
|
+
dragging = null;
|
600
|
+
draggingHeight = null;
|
601
|
+
});
|
602
|
+
// Handle drop event on sortable & placeholder
|
603
|
+
// TODO: REMOVE placeholder?????
|
604
|
+
_on([sortableElement, placeholder], 'drop', function(e) {
|
605
|
+
var visiblePlaceholder;
|
606
|
+
if (!_listsConnected(sortableElement, dragging.parentElement)) {
|
607
|
+
return;
|
608
|
+
}
|
609
|
+
|
610
|
+
e.preventDefault();
|
611
|
+
e.stopPropagation();
|
612
|
+
visiblePlaceholder = placeholders.filter(_attached)[0];
|
613
|
+
_after(visiblePlaceholder, dragging);
|
614
|
+
dragging.dispatchEvent(_makeEvent('dragend'));
|
615
|
+
});
|
616
|
+
|
617
|
+
// Handle dragover and dragenter events on draggable items
|
618
|
+
var onDragOverEnter = function(e) {
|
619
|
+
if (!_listsConnected(sortableElement, dragging.parentElement)) {
|
620
|
+
return;
|
621
|
+
}
|
622
|
+
|
623
|
+
e.preventDefault();
|
624
|
+
e.stopPropagation();
|
625
|
+
e.dataTransfer.dropEffect = 'move';
|
626
|
+
if (items.indexOf(this) !== -1) {
|
627
|
+
var thisHeight = parseInt(window.getComputedStyle(this).height);
|
628
|
+
var placeholderIndex = _index(placeholder);
|
629
|
+
var thisIndex = _index(this);
|
630
|
+
if (options.forcePlaceholderSize) {
|
631
|
+
placeholder.style.height = draggingHeight + 'px';
|
632
|
+
}
|
633
|
+
|
634
|
+
// Check if `this` is bigger than the draggable. If it is, we have to define a dead zone to prevent flickering
|
635
|
+
if (thisHeight > draggingHeight) {
|
636
|
+
// Dead zone?
|
637
|
+
var deadZone = thisHeight - draggingHeight;
|
638
|
+
var offsetTop = _offset(this).top;
|
639
|
+
if (placeholderIndex < thisIndex &&
|
640
|
+
e.pageY < offsetTop + deadZone) {
|
641
|
+
return;
|
642
|
+
}
|
643
|
+
if (placeholderIndex > thisIndex &&
|
644
|
+
e.pageY > offsetTop + thisHeight - deadZone) {
|
645
|
+
return;
|
646
|
+
}
|
647
|
+
}
|
648
|
+
|
649
|
+
if (dragging.oldDisplay === undefined) {
|
650
|
+
dragging.oldDisplay = dragging.style.display;
|
651
|
+
}
|
652
|
+
dragging.style.display = 'none';
|
653
|
+
if (placeholderIndex < thisIndex) {
|
654
|
+
_after(this, placeholder);
|
655
|
+
} else {
|
656
|
+
_before(this, placeholder);
|
657
|
+
}
|
658
|
+
// Intentionally violated chaining, it is more complex otherwise
|
659
|
+
placeholders
|
660
|
+
.filter(function(element) {return element !== placeholder;})
|
661
|
+
.forEach(_detach);
|
662
|
+
} else {
|
663
|
+
if (placeholders.indexOf(this) === -1 &&
|
664
|
+
!_filter(this.children, options.items).length) {
|
665
|
+
placeholders.forEach(_detach);
|
666
|
+
this.appendChild(placeholder);
|
667
|
+
}
|
668
|
+
}
|
669
|
+
};
|
670
|
+
_on(items.concat(sortableElement), 'dragover', onDragOverEnter);
|
671
|
+
_on(items.concat(sortableElement), 'dragenter', onDragOverEnter);
|
672
|
+
});
|
673
|
+
|
674
|
+
return sortableElements;
|
675
|
+
};
|
676
|
+
|
677
|
+
sortable.destroy = function(sortableElement) {
|
678
|
+
_destroySortable(sortableElement);
|
679
|
+
};
|
680
|
+
|
681
|
+
sortable.enable = function(sortableElement) {
|
682
|
+
_enableSortable(sortableElement);
|
683
|
+
};
|
684
|
+
|
685
|
+
sortable.disable = function(sortableElement) {
|
686
|
+
_disableSortable(sortableElement);
|
687
|
+
};
|
688
|
+
|
689
|
+
|
690
|
+
return sortable;
|
691
|
+
}));
|