promethee 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/assets/stylesheets/promethee-edit/_move.sass +6 -4
- data/app/views/promethee/_edit.html.erb +14 -6
- data/app/views/promethee/components/aside/_edit.define.html.erb +14 -151
- data/app/views/promethee/components/aside/_icon.svg +3 -0
- data/app/views/promethee/components/column/_edit.define.html.erb +17 -31
- data/app/views/promethee/components/column/_icon.svg +3 -0
- data/app/views/promethee/components/cover/_edit.define.html.erb +16 -151
- data/app/views/promethee/components/cover/_icon.svg +3 -0
- data/app/views/promethee/components/image/_edit.define.html.erb +17 -149
- data/app/views/promethee/components/image/_icon.svg +3 -0
- data/app/views/promethee/components/row/_edit.define.html.erb +14 -150
- data/app/views/promethee/components/row/_icon.svg +3 -0
- data/app/views/promethee/components/slider/_edit.define.html.erb +14 -150
- data/app/views/promethee/components/slider/_edit.inspect.html.erb +43 -1
- data/app/views/promethee/components/slider/_edit.move.html.erb +6 -2
- data/app/views/promethee/components/slider/_icon.svg +3 -0
- data/app/views/promethee/components/text/_edit.define.html.erb +15 -150
- data/app/views/promethee/components/text/_icon.svg +3 -0
- data/app/views/promethee/components/video/_edit.define.html.erb +14 -29
- data/app/views/promethee/components/video/_icon.svg +3 -0
- data/app/views/promethee/edit/_move.html.erb +48 -21
- data/app/views/promethee/presets/_icon.image-with-text.svg +3 -0
- data/app/views/promethee/presets/_image-with-text.html.erb +33 -0
- data/app/views/promethee/utils/_summernote-config.html.erb +11 -5
- data/lib/promethee/rails/helper.rb +4 -0
- data/lib/promethee/rails/version.rb +1 -1
- data/vendor/assets/javascripts/ui-sortable.js +683 -0
- metadata +14 -3
@@ -2,15 +2,21 @@
|
|
2
2
|
promethee.constant('summernoteConfig', {
|
3
3
|
disableDragAndDrop: true,
|
4
4
|
callbacks: {
|
5
|
+
// Remove text styles on paste
|
5
6
|
onPaste: function(event) {
|
6
|
-
|
7
|
+
event.preventDefault();
|
8
|
+
|
9
|
+
// Get and trim clipboard content as paragraph
|
7
10
|
var paragraph = document.createElement('p');
|
8
11
|
paragraph.textContent = ((event.originalEvent || event).clipboardData || window.clipboardData).getData('Text').trim();
|
9
|
-
event.preventDefault();
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
// Delete selection if anything is selected (expected behaviour on paste)
|
14
|
+
if((window.getSelection !== undefined ? window.getSelection() : document.selection.createRange()).toString().length > 0) {
|
15
|
+
document.execCommand('delete', false);
|
16
|
+
}
|
17
|
+
|
18
|
+
// Insert trimmed clipboard content as paragraph
|
19
|
+
document.execCommand('insertHTML', false, paragraph.outerHTML);
|
14
20
|
}
|
15
21
|
},
|
16
22
|
toolbar: [
|
@@ -22,6 +22,10 @@ module Promethee::Rails::Helper
|
|
22
22
|
promethee_partials_for 'utils/_*.html.erb'
|
23
23
|
end
|
24
24
|
|
25
|
+
def promethee_preset_partials
|
26
|
+
promethee_partials_for 'presets/_*.html.erb'
|
27
|
+
end
|
28
|
+
|
25
29
|
# promethee_bem_classes 'promethee-edit__move__droppable', '--{{type}}', '--first'
|
26
30
|
# -> promethee-edit__move__droppable promethee-edit__move__droppable--{{type}} promethee-edit__move__droppable--{{type}}--first"
|
27
31
|
def promethee_bem_classes(*args)
|
@@ -0,0 +1,683 @@
|
|
1
|
+
/*
|
2
|
+
jQuery UI Sortable plugin wrapper
|
3
|
+
@param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
|
4
|
+
*/
|
5
|
+
angular
|
6
|
+
.module('ui.sortable', [])
|
7
|
+
.value('uiSortableConfig', {
|
8
|
+
// the default for jquery-ui sortable is "> *", we need to restrict this to
|
9
|
+
// ng-repeat items
|
10
|
+
// if the user uses
|
11
|
+
items: '> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]'
|
12
|
+
})
|
13
|
+
.directive('uiSortable', [
|
14
|
+
'uiSortableConfig',
|
15
|
+
'$timeout',
|
16
|
+
'$log',
|
17
|
+
function(uiSortableConfig, $timeout, $log) {
|
18
|
+
return {
|
19
|
+
require: '?ngModel',
|
20
|
+
scope: {
|
21
|
+
ngModel: '=',
|
22
|
+
uiSortable: '=',
|
23
|
+
////Expression bindings from html.
|
24
|
+
create: '&uiSortableCreate',
|
25
|
+
// helper:'&uiSortableHelper',
|
26
|
+
start: '&uiSortableStart',
|
27
|
+
activate: '&uiSortableActivate',
|
28
|
+
// sort:'&uiSortableSort',
|
29
|
+
// change:'&uiSortableChange',
|
30
|
+
// over:'&uiSortableOver',
|
31
|
+
// out:'&uiSortableOut',
|
32
|
+
beforeStop: '&uiSortableBeforeStop',
|
33
|
+
update: '&uiSortableUpdate',
|
34
|
+
remove: '&uiSortableRemove',
|
35
|
+
receive: '&uiSortableReceive',
|
36
|
+
deactivate: '&uiSortableDeactivate',
|
37
|
+
stop: '&uiSortableStop'
|
38
|
+
},
|
39
|
+
link: function(scope, element, attrs, ngModel) {
|
40
|
+
var savedNodes;
|
41
|
+
var helper;
|
42
|
+
|
43
|
+
function combineCallbacks(first, second) {
|
44
|
+
var firstIsFunc = typeof first === 'function';
|
45
|
+
var secondIsFunc = typeof second === 'function';
|
46
|
+
if (firstIsFunc && secondIsFunc) {
|
47
|
+
return function() {
|
48
|
+
first.apply(this, arguments);
|
49
|
+
second.apply(this, arguments);
|
50
|
+
};
|
51
|
+
} else if (secondIsFunc) {
|
52
|
+
return second;
|
53
|
+
}
|
54
|
+
return first;
|
55
|
+
}
|
56
|
+
|
57
|
+
function getSortableWidgetInstance(element) {
|
58
|
+
// this is a fix to support jquery-ui prior to v1.11.x
|
59
|
+
// otherwise we should be using `element.sortable('instance')`
|
60
|
+
var data = element.data('ui-sortable');
|
61
|
+
if (
|
62
|
+
data &&
|
63
|
+
typeof data === 'object' &&
|
64
|
+
data.widgetFullName === 'ui-sortable'
|
65
|
+
) {
|
66
|
+
return data;
|
67
|
+
}
|
68
|
+
return null;
|
69
|
+
}
|
70
|
+
|
71
|
+
function setItemChildrenWidth(item) {
|
72
|
+
item.children().each(function() {
|
73
|
+
var $el = angular.element(this);
|
74
|
+
|
75
|
+
// Preserve the with of the element
|
76
|
+
$el.width($el.width());
|
77
|
+
});
|
78
|
+
}
|
79
|
+
|
80
|
+
function dummyHelper(e, item) {
|
81
|
+
return item;
|
82
|
+
}
|
83
|
+
|
84
|
+
function patchSortableOption(key, value) {
|
85
|
+
if (callbacks[key]) {
|
86
|
+
if (key === 'stop') {
|
87
|
+
// call apply after stop
|
88
|
+
value = combineCallbacks(value, function() {
|
89
|
+
scope.$apply();
|
90
|
+
});
|
91
|
+
|
92
|
+
value = combineCallbacks(value, afterStop);
|
93
|
+
}
|
94
|
+
// wrap the callback
|
95
|
+
value = combineCallbacks(callbacks[key], value);
|
96
|
+
} else if (wrappers[key]) {
|
97
|
+
value = wrappers[key](value);
|
98
|
+
}
|
99
|
+
|
100
|
+
// patch the options that need to have values set
|
101
|
+
if (!value && (key === 'items' || key === 'ui-model-items')) {
|
102
|
+
value = uiSortableConfig.items;
|
103
|
+
}
|
104
|
+
|
105
|
+
return value;
|
106
|
+
}
|
107
|
+
|
108
|
+
function patchUISortableOptions(
|
109
|
+
newOpts,
|
110
|
+
oldOpts,
|
111
|
+
sortableWidgetInstance
|
112
|
+
) {
|
113
|
+
function addDummyOptionKey(value, key) {
|
114
|
+
if (!(key in opts)) {
|
115
|
+
// add the key in the opts object so that
|
116
|
+
// the patch function detects and handles it
|
117
|
+
opts[key] = null;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
// for this directive to work we have to attach some callbacks
|
121
|
+
angular.forEach(callbacks, addDummyOptionKey);
|
122
|
+
|
123
|
+
// only initialize it in case we have to
|
124
|
+
// update some options of the sortable
|
125
|
+
var optsDiff = null;
|
126
|
+
|
127
|
+
if (oldOpts) {
|
128
|
+
// reset deleted options to default
|
129
|
+
var defaultOptions;
|
130
|
+
angular.forEach(oldOpts, function(oldValue, key) {
|
131
|
+
if (!newOpts || !(key in newOpts)) {
|
132
|
+
if (key in directiveOpts) {
|
133
|
+
if (key === 'ui-floating') {
|
134
|
+
opts[key] = 'auto';
|
135
|
+
} else {
|
136
|
+
opts[key] = patchSortableOption(key, undefined);
|
137
|
+
}
|
138
|
+
return;
|
139
|
+
}
|
140
|
+
|
141
|
+
if (!defaultOptions) {
|
142
|
+
defaultOptions = angular.element.ui.sortable().options;
|
143
|
+
}
|
144
|
+
var defaultValue = defaultOptions[key];
|
145
|
+
defaultValue = patchSortableOption(key, defaultValue);
|
146
|
+
|
147
|
+
if (!optsDiff) {
|
148
|
+
optsDiff = {};
|
149
|
+
}
|
150
|
+
optsDiff[key] = defaultValue;
|
151
|
+
opts[key] = defaultValue;
|
152
|
+
}
|
153
|
+
});
|
154
|
+
}
|
155
|
+
|
156
|
+
newOpts = angular.extend({}, newOpts);
|
157
|
+
// update changed options
|
158
|
+
// handle the custom option of the directive first
|
159
|
+
angular.forEach(newOpts, function(value, key) {
|
160
|
+
if (key in directiveOpts) {
|
161
|
+
if (
|
162
|
+
key === 'ui-floating' &&
|
163
|
+
(value === false || value === true) &&
|
164
|
+
sortableWidgetInstance
|
165
|
+
) {
|
166
|
+
sortableWidgetInstance.floating = value;
|
167
|
+
}
|
168
|
+
|
169
|
+
if (
|
170
|
+
key === 'ui-preserve-size' &&
|
171
|
+
(value === false || value === true)
|
172
|
+
) {
|
173
|
+
var userProvidedHelper = opts.helper;
|
174
|
+
newOpts.helper = function(e, item) {
|
175
|
+
if (opts['ui-preserve-size'] === true) {
|
176
|
+
setItemChildrenWidth(item);
|
177
|
+
}
|
178
|
+
return (userProvidedHelper || dummyHelper).apply(
|
179
|
+
this,
|
180
|
+
arguments
|
181
|
+
);
|
182
|
+
};
|
183
|
+
}
|
184
|
+
|
185
|
+
opts[key] = patchSortableOption(key, value);
|
186
|
+
}
|
187
|
+
});
|
188
|
+
|
189
|
+
// handle the normal option of the directive
|
190
|
+
angular.forEach(newOpts, function(value, key) {
|
191
|
+
if (key in directiveOpts) {
|
192
|
+
// the custom option of the directive are already handled
|
193
|
+
return;
|
194
|
+
}
|
195
|
+
|
196
|
+
value = patchSortableOption(key, value);
|
197
|
+
|
198
|
+
if (!optsDiff) {
|
199
|
+
optsDiff = {};
|
200
|
+
}
|
201
|
+
optsDiff[key] = value;
|
202
|
+
opts[key] = value;
|
203
|
+
});
|
204
|
+
|
205
|
+
return optsDiff;
|
206
|
+
}
|
207
|
+
|
208
|
+
function getPlaceholderElement(element) {
|
209
|
+
var placeholder = element.sortable('option', 'placeholder');
|
210
|
+
|
211
|
+
// placeholder.element will be a function if the placeholder, has
|
212
|
+
// been created (placeholder will be an object). If it hasn't
|
213
|
+
// been created, either placeholder will be false if no
|
214
|
+
// placeholder class was given or placeholder.element will be
|
215
|
+
// undefined if a class was given (placeholder will be a string)
|
216
|
+
if (
|
217
|
+
placeholder &&
|
218
|
+
placeholder.element &&
|
219
|
+
typeof placeholder.element === 'function'
|
220
|
+
) {
|
221
|
+
var result = placeholder.element();
|
222
|
+
// workaround for jquery ui 1.9.x,
|
223
|
+
// not returning jquery collection
|
224
|
+
result = angular.element(result);
|
225
|
+
return result;
|
226
|
+
}
|
227
|
+
return null;
|
228
|
+
}
|
229
|
+
|
230
|
+
function getPlaceholderExcludesludes(element, placeholder) {
|
231
|
+
// exact match with the placeholder's class attribute to handle
|
232
|
+
// the case that multiple connected sortables exist and
|
233
|
+
// the placeholder option equals the class of sortable items
|
234
|
+
var notCssSelector = opts['ui-model-items'].replace(/[^,]*>/g, '');
|
235
|
+
var excludes = element.find(
|
236
|
+
'[class="' +
|
237
|
+
placeholder.attr('class') +
|
238
|
+
'"]:not(' +
|
239
|
+
notCssSelector +
|
240
|
+
')'
|
241
|
+
);
|
242
|
+
return excludes;
|
243
|
+
}
|
244
|
+
|
245
|
+
function hasSortingHelper(element, ui) {
|
246
|
+
var helperOption = element.sortable('option', 'helper');
|
247
|
+
return (
|
248
|
+
helperOption === 'clone' ||
|
249
|
+
(typeof helperOption === 'function' &&
|
250
|
+
ui.item.sortable.isCustomHelperUsed())
|
251
|
+
);
|
252
|
+
}
|
253
|
+
|
254
|
+
function getSortingHelper(element, ui /*, savedNodes*/) {
|
255
|
+
var result = null;
|
256
|
+
if (
|
257
|
+
hasSortingHelper(element, ui) &&
|
258
|
+
element.sortable('option', 'appendTo') === 'parent'
|
259
|
+
) {
|
260
|
+
// The .ui-sortable-helper element (that's the default class name)
|
261
|
+
result = helper;
|
262
|
+
}
|
263
|
+
return result;
|
264
|
+
}
|
265
|
+
|
266
|
+
// thanks jquery-ui
|
267
|
+
function isFloating(item) {
|
268
|
+
return (
|
269
|
+
/left|right/.test(item.css('float')) ||
|
270
|
+
/inline|table-cell/.test(item.css('display'))
|
271
|
+
);
|
272
|
+
}
|
273
|
+
|
274
|
+
function getElementContext(elementScopes, element) {
|
275
|
+
for (var i = 0; i < elementScopes.length; i++) {
|
276
|
+
var c = elementScopes[i];
|
277
|
+
if (c.element[0] === element[0]) {
|
278
|
+
return c;
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
function afterStop(e, ui) {
|
284
|
+
ui.item.sortable._destroy();
|
285
|
+
}
|
286
|
+
|
287
|
+
// return the index of ui.item among the items
|
288
|
+
// we can't just do ui.item.index() because there it might have siblings
|
289
|
+
// which are not items
|
290
|
+
function getItemIndex(item) {
|
291
|
+
return item
|
292
|
+
.parent()
|
293
|
+
.find(opts['ui-model-items'])
|
294
|
+
.index(item);
|
295
|
+
}
|
296
|
+
|
297
|
+
var opts = {};
|
298
|
+
|
299
|
+
// directive specific options
|
300
|
+
var directiveOpts = {
|
301
|
+
'ui-floating': undefined,
|
302
|
+
'ui-model-items': uiSortableConfig.items,
|
303
|
+
'ui-preserve-size': undefined
|
304
|
+
};
|
305
|
+
|
306
|
+
var callbacks = {
|
307
|
+
create: null,
|
308
|
+
start: null,
|
309
|
+
activate: null,
|
310
|
+
// sort: null,
|
311
|
+
// change: null,
|
312
|
+
// over: null,
|
313
|
+
// out: null,
|
314
|
+
beforeStop: null,
|
315
|
+
update: null,
|
316
|
+
remove: null,
|
317
|
+
receive: null,
|
318
|
+
deactivate: null,
|
319
|
+
stop: null
|
320
|
+
};
|
321
|
+
|
322
|
+
var wrappers = {
|
323
|
+
helper: null
|
324
|
+
};
|
325
|
+
|
326
|
+
angular.extend(
|
327
|
+
opts,
|
328
|
+
directiveOpts,
|
329
|
+
uiSortableConfig,
|
330
|
+
scope.uiSortable
|
331
|
+
);
|
332
|
+
|
333
|
+
if (!angular.element.fn || !angular.element.fn.jquery) {
|
334
|
+
$log.error(
|
335
|
+
'ui.sortable: jQuery should be included before AngularJS!'
|
336
|
+
);
|
337
|
+
return;
|
338
|
+
}
|
339
|
+
|
340
|
+
function wireUp() {
|
341
|
+
// When we add or remove elements, we need the sortable to 'refresh'
|
342
|
+
// so it can find the new/removed elements.
|
343
|
+
scope.$watchCollection('ngModel', function() {
|
344
|
+
// Timeout to let ng-repeat modify the DOM
|
345
|
+
$timeout(
|
346
|
+
function() {
|
347
|
+
// ensure that the jquery-ui-sortable widget instance
|
348
|
+
// is still bound to the directive's element
|
349
|
+
if (!!getSortableWidgetInstance(element)) {
|
350
|
+
element.sortable('refresh');
|
351
|
+
}
|
352
|
+
},
|
353
|
+
0,
|
354
|
+
false
|
355
|
+
);
|
356
|
+
});
|
357
|
+
|
358
|
+
callbacks.start = function(e, ui) {
|
359
|
+
if (opts['ui-floating'] === 'auto') {
|
360
|
+
// since the drag has started, the element will be
|
361
|
+
// absolutely positioned, so we check its siblings
|
362
|
+
var siblings = ui.item.siblings();
|
363
|
+
var sortableWidgetInstance = getSortableWidgetInstance(
|
364
|
+
angular.element(e.target)
|
365
|
+
);
|
366
|
+
sortableWidgetInstance.floating = isFloating(siblings);
|
367
|
+
}
|
368
|
+
|
369
|
+
// Save the starting position of dragged item
|
370
|
+
var index = getItemIndex(ui.item);
|
371
|
+
ui.item.sortable = {
|
372
|
+
model: ngModel.$modelValue[index],
|
373
|
+
index: index,
|
374
|
+
source: element,
|
375
|
+
sourceList: ui.item.parent(),
|
376
|
+
sourceModel: ngModel.$modelValue,
|
377
|
+
cancel: function() {
|
378
|
+
ui.item.sortable._isCanceled = true;
|
379
|
+
},
|
380
|
+
isCanceled: function() {
|
381
|
+
return ui.item.sortable._isCanceled;
|
382
|
+
},
|
383
|
+
isCustomHelperUsed: function() {
|
384
|
+
return !!ui.item.sortable._isCustomHelperUsed;
|
385
|
+
},
|
386
|
+
_isCanceled: false,
|
387
|
+
_isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed,
|
388
|
+
_destroy: function() {
|
389
|
+
angular.forEach(ui.item.sortable, function(value, key) {
|
390
|
+
ui.item.sortable[key] = undefined;
|
391
|
+
});
|
392
|
+
},
|
393
|
+
_connectedSortables: [],
|
394
|
+
_getElementContext: function(element) {
|
395
|
+
return getElementContext(this._connectedSortables, element);
|
396
|
+
}
|
397
|
+
};
|
398
|
+
};
|
399
|
+
|
400
|
+
callbacks.activate = function(e, ui) {
|
401
|
+
var isSourceContext = ui.item.sortable.source === element;
|
402
|
+
var savedNodesOrigin = isSourceContext
|
403
|
+
? ui.item.sortable.sourceList
|
404
|
+
: element;
|
405
|
+
var elementContext = {
|
406
|
+
element: element,
|
407
|
+
scope: scope,
|
408
|
+
isSourceContext: isSourceContext,
|
409
|
+
savedNodesOrigin: savedNodesOrigin
|
410
|
+
};
|
411
|
+
// save the directive's scope so that it is accessible from ui.item.sortable
|
412
|
+
ui.item.sortable._connectedSortables.push(elementContext);
|
413
|
+
|
414
|
+
// We need to make a copy of the current element's contents so
|
415
|
+
// we can restore it after sortable has messed it up.
|
416
|
+
// This is inside activate (instead of start) in order to save
|
417
|
+
// both lists when dragging between connected lists.
|
418
|
+
savedNodes = savedNodesOrigin.contents();
|
419
|
+
helper = ui.helper;
|
420
|
+
|
421
|
+
// If this list has a placeholder (the connected lists won't),
|
422
|
+
// don't inlcude it in saved nodes.
|
423
|
+
var placeholder = getPlaceholderElement(element);
|
424
|
+
if (placeholder && placeholder.length) {
|
425
|
+
var excludes = getPlaceholderExcludesludes(
|
426
|
+
element,
|
427
|
+
placeholder
|
428
|
+
);
|
429
|
+
savedNodes = savedNodes.not(excludes);
|
430
|
+
}
|
431
|
+
};
|
432
|
+
|
433
|
+
callbacks.update = function(e, ui) {
|
434
|
+
// Save current drop position but only if this is not a second
|
435
|
+
// update that happens when moving between lists because then
|
436
|
+
// the value will be overwritten with the old value
|
437
|
+
if (!ui.item.sortable.received) {
|
438
|
+
ui.item.sortable.dropindex = getItemIndex(ui.item);
|
439
|
+
var droptarget = ui.item
|
440
|
+
.parent()
|
441
|
+
.closest(
|
442
|
+
'[ui-sortable], [data-ui-sortable], [x-ui-sortable]'
|
443
|
+
);
|
444
|
+
ui.item.sortable.droptarget = droptarget;
|
445
|
+
ui.item.sortable.droptargetList = ui.item.parent();
|
446
|
+
|
447
|
+
var droptargetContext = ui.item.sortable._getElementContext(
|
448
|
+
droptarget
|
449
|
+
);
|
450
|
+
ui.item.sortable.droptargetModel =
|
451
|
+
droptargetContext.scope.ngModel;
|
452
|
+
|
453
|
+
// Cancel the sort (let ng-repeat do the sort for us)
|
454
|
+
// Don't cancel if this is the received list because it has
|
455
|
+
// already been canceled in the other list, and trying to cancel
|
456
|
+
// here will mess up the DOM.
|
457
|
+
element.sortable('cancel');
|
458
|
+
}
|
459
|
+
|
460
|
+
// Put the nodes back exactly the way they started (this is very
|
461
|
+
// important because ng-repeat uses comment elements to delineate
|
462
|
+
// the start and stop of repeat sections and sortable doesn't
|
463
|
+
// respect their order (even if we cancel, the order of the
|
464
|
+
// comments are still messed up).
|
465
|
+
var sortingHelper =
|
466
|
+
!ui.item.sortable.received &&
|
467
|
+
getSortingHelper(element, ui, savedNodes);
|
468
|
+
if (sortingHelper && sortingHelper.length) {
|
469
|
+
// Restore all the savedNodes except from the sorting helper element.
|
470
|
+
// That way it will be garbage collected.
|
471
|
+
savedNodes = savedNodes.not(sortingHelper);
|
472
|
+
}
|
473
|
+
var elementContext = ui.item.sortable._getElementContext(element);
|
474
|
+
savedNodes.appendTo(elementContext.savedNodesOrigin);
|
475
|
+
|
476
|
+
// If this is the target connected list then
|
477
|
+
// it's safe to clear the restored nodes since:
|
478
|
+
// update is currently running and
|
479
|
+
// stop is not called for the target list.
|
480
|
+
if (ui.item.sortable.received) {
|
481
|
+
savedNodes = null;
|
482
|
+
}
|
483
|
+
|
484
|
+
// If received is true (an item was dropped in from another list)
|
485
|
+
// then we add the new item to this list otherwise wait until the
|
486
|
+
// stop event where we will know if it was a sort or item was
|
487
|
+
// moved here from another list
|
488
|
+
if (ui.item.sortable.received && !ui.item.sortable.isCanceled()) {
|
489
|
+
scope.$apply(function() {
|
490
|
+
ngModel.$modelValue.splice(
|
491
|
+
ui.item.sortable.dropindex,
|
492
|
+
0,
|
493
|
+
ui.item.sortable.moved
|
494
|
+
);
|
495
|
+
});
|
496
|
+
scope.$emit('ui-sortable:moved', ui);
|
497
|
+
}
|
498
|
+
};
|
499
|
+
|
500
|
+
callbacks.stop = function(e, ui) {
|
501
|
+
// If the received flag hasn't be set on the item, this is a
|
502
|
+
// normal sort, if dropindex is set, the item was moved, so move
|
503
|
+
// the items in the list.
|
504
|
+
var wasMoved =
|
505
|
+
'dropindex' in ui.item.sortable &&
|
506
|
+
!ui.item.sortable.isCanceled();
|
507
|
+
|
508
|
+
if (wasMoved && !ui.item.sortable.received) {
|
509
|
+
scope.$apply(function() {
|
510
|
+
ngModel.$modelValue.splice(
|
511
|
+
ui.item.sortable.dropindex,
|
512
|
+
0,
|
513
|
+
ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0]
|
514
|
+
);
|
515
|
+
});
|
516
|
+
scope.$emit('ui-sortable:moved', ui);
|
517
|
+
} else if (
|
518
|
+
!wasMoved &&
|
519
|
+
!angular.equals(
|
520
|
+
element.contents().toArray(),
|
521
|
+
savedNodes.toArray()
|
522
|
+
)
|
523
|
+
) {
|
524
|
+
// if the item was not moved
|
525
|
+
// and the DOM element order has changed,
|
526
|
+
// then restore the elements
|
527
|
+
// so that the ngRepeat's comment are correct.
|
528
|
+
|
529
|
+
var sortingHelper = getSortingHelper(element, ui, savedNodes);
|
530
|
+
if (sortingHelper && sortingHelper.length) {
|
531
|
+
// Restore all the savedNodes except from the sorting helper element.
|
532
|
+
// That way it will be garbage collected.
|
533
|
+
savedNodes = savedNodes.not(sortingHelper);
|
534
|
+
}
|
535
|
+
var elementContext = ui.item.sortable._getElementContext(
|
536
|
+
element
|
537
|
+
);
|
538
|
+
savedNodes.appendTo(elementContext.savedNodesOrigin);
|
539
|
+
}
|
540
|
+
|
541
|
+
// It's now safe to clear the savedNodes and helper
|
542
|
+
// since stop is the last callback.
|
543
|
+
savedNodes = null;
|
544
|
+
helper = null;
|
545
|
+
};
|
546
|
+
|
547
|
+
callbacks.receive = function(e, ui) {
|
548
|
+
// An item was dropped here from another list, set a flag on the
|
549
|
+
// item.
|
550
|
+
ui.item.sortable.received = true;
|
551
|
+
};
|
552
|
+
|
553
|
+
callbacks.remove = function(e, ui) {
|
554
|
+
// Workaround for a problem observed in nested connected lists.
|
555
|
+
// There should be an 'update' event before 'remove' when moving
|
556
|
+
// elements. If the event did not fire, cancel sorting.
|
557
|
+
if (!('dropindex' in ui.item.sortable)) {
|
558
|
+
element.sortable('cancel');
|
559
|
+
ui.item.sortable.cancel();
|
560
|
+
}
|
561
|
+
|
562
|
+
// Remove the item from this list's model and copy data into item,
|
563
|
+
// so the next list can retrive it
|
564
|
+
if (!ui.item.sortable.isCanceled()) {
|
565
|
+
scope.$apply(function() {
|
566
|
+
ui.item.sortable.moved = ngModel.$modelValue.splice(
|
567
|
+
ui.item.sortable.index,
|
568
|
+
1
|
569
|
+
)[0];
|
570
|
+
});
|
571
|
+
}
|
572
|
+
};
|
573
|
+
|
574
|
+
// setup attribute handlers
|
575
|
+
angular.forEach(callbacks, function(value, key) {
|
576
|
+
callbacks[key] = combineCallbacks(callbacks[key], function() {
|
577
|
+
var attrHandler = scope[key];
|
578
|
+
var attrHandlerFn;
|
579
|
+
if (
|
580
|
+
typeof attrHandler === 'function' &&
|
581
|
+
(
|
582
|
+
'uiSortable' +
|
583
|
+
key.substring(0, 1).toUpperCase() +
|
584
|
+
key.substring(1)
|
585
|
+
).length &&
|
586
|
+
typeof (attrHandlerFn = attrHandler()) === 'function'
|
587
|
+
) {
|
588
|
+
attrHandlerFn.apply(this, arguments);
|
589
|
+
}
|
590
|
+
});
|
591
|
+
});
|
592
|
+
|
593
|
+
wrappers.helper = function(inner) {
|
594
|
+
if (inner && typeof inner === 'function') {
|
595
|
+
return function(e, item) {
|
596
|
+
var oldItemSortable = item.sortable;
|
597
|
+
var index = getItemIndex(item);
|
598
|
+
|
599
|
+
item.sortable = {
|
600
|
+
model: ngModel.$modelValue[index],
|
601
|
+
index: index,
|
602
|
+
source: element,
|
603
|
+
sourceList: item.parent(),
|
604
|
+
sourceModel: ngModel.$modelValue,
|
605
|
+
_restore: function() {
|
606
|
+
angular.forEach(item.sortable, function(value, key) {
|
607
|
+
item.sortable[key] = undefined;
|
608
|
+
});
|
609
|
+
|
610
|
+
item.sortable = oldItemSortable;
|
611
|
+
}
|
612
|
+
};
|
613
|
+
|
614
|
+
var innerResult = inner.apply(this, arguments);
|
615
|
+
item.sortable._restore();
|
616
|
+
item.sortable._isCustomHelperUsed = item !== innerResult;
|
617
|
+
return innerResult;
|
618
|
+
};
|
619
|
+
}
|
620
|
+
return inner;
|
621
|
+
};
|
622
|
+
|
623
|
+
scope.$watchCollection(
|
624
|
+
'uiSortable',
|
625
|
+
function(newOpts, oldOpts) {
|
626
|
+
// ensure that the jquery-ui-sortable widget instance
|
627
|
+
// is still bound to the directive's element
|
628
|
+
var sortableWidgetInstance = getSortableWidgetInstance(element);
|
629
|
+
if (!!sortableWidgetInstance) {
|
630
|
+
var optsDiff = patchUISortableOptions(
|
631
|
+
newOpts,
|
632
|
+
oldOpts,
|
633
|
+
sortableWidgetInstance
|
634
|
+
);
|
635
|
+
|
636
|
+
if (optsDiff) {
|
637
|
+
element.sortable('option', optsDiff);
|
638
|
+
}
|
639
|
+
}
|
640
|
+
},
|
641
|
+
true
|
642
|
+
);
|
643
|
+
|
644
|
+
patchUISortableOptions(opts);
|
645
|
+
}
|
646
|
+
|
647
|
+
function init() {
|
648
|
+
if (ngModel) {
|
649
|
+
wireUp();
|
650
|
+
} else {
|
651
|
+
$log.info('ui.sortable: ngModel not provided!', element);
|
652
|
+
}
|
653
|
+
|
654
|
+
// Create sortable
|
655
|
+
element.sortable(opts);
|
656
|
+
}
|
657
|
+
|
658
|
+
function initIfEnabled() {
|
659
|
+
if (scope.uiSortable && scope.uiSortable.disabled) {
|
660
|
+
return false;
|
661
|
+
}
|
662
|
+
|
663
|
+
init();
|
664
|
+
|
665
|
+
// Stop Watcher
|
666
|
+
initIfEnabled.cancelWatcher();
|
667
|
+
initIfEnabled.cancelWatcher = angular.noop;
|
668
|
+
|
669
|
+
return true;
|
670
|
+
}
|
671
|
+
|
672
|
+
initIfEnabled.cancelWatcher = angular.noop;
|
673
|
+
|
674
|
+
if (!initIfEnabled()) {
|
675
|
+
initIfEnabled.cancelWatcher = scope.$watch(
|
676
|
+
'uiSortable.disabled',
|
677
|
+
initIfEnabled
|
678
|
+
);
|
679
|
+
}
|
680
|
+
}
|
681
|
+
};
|
682
|
+
}
|
683
|
+
]);
|