django-fast-treenode 2.1.4__py3-none-any.whl → 3.0.0__py3-none-any.whl
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.
- django_fast_treenode-3.0.0.dist-info/METADATA +203 -0
- django_fast_treenode-3.0.0.dist-info/RECORD +90 -0
- {django_fast_treenode-2.1.4.dist-info → django_fast_treenode-3.0.0.dist-info}/WHEEL +1 -1
- treenode/admin/__init__.py +2 -7
- treenode/admin/admin.py +138 -209
- treenode/admin/changelist.py +21 -39
- treenode/admin/exporter.py +170 -0
- treenode/admin/importer.py +171 -0
- treenode/admin/mixin.py +291 -0
- treenode/apps.py +42 -20
- treenode/cache.py +192 -303
- treenode/forms.py +45 -65
- treenode/managers/__init__.py +4 -20
- treenode/managers/managers.py +216 -0
- treenode/managers/queries.py +233 -0
- treenode/managers/tasks.py +167 -0
- treenode/models/__init__.py +8 -5
- treenode/models/decorators.py +54 -0
- treenode/models/factory.py +44 -68
- treenode/models/mixins/__init__.py +2 -1
- treenode/models/mixins/ancestors.py +44 -20
- treenode/models/mixins/children.py +33 -26
- treenode/models/mixins/descendants.py +33 -22
- treenode/models/mixins/family.py +25 -15
- treenode/models/mixins/logical.py +23 -21
- treenode/models/mixins/node.py +162 -104
- treenode/models/mixins/properties.py +22 -16
- treenode/models/mixins/roots.py +59 -15
- treenode/models/mixins/siblings.py +46 -43
- treenode/models/mixins/tree.py +212 -153
- treenode/models/mixins/update.py +154 -0
- treenode/models/models.py +365 -0
- treenode/settings.py +28 -0
- treenode/static/{treenode/css → css}/tree_widget.css +1 -1
- treenode/static/{treenode/css → css}/treenode_admin.css +43 -2
- treenode/static/css/treenode_tabs.css +51 -0
- treenode/static/js/lz-string.min.js +1 -0
- treenode/static/{treenode/js → js}/tree_widget.js +9 -23
- treenode/static/js/treenode_admin.js +531 -0
- treenode/static/vendors/jquery-ui/AUTHORS.txt +384 -0
- treenode/static/vendors/jquery-ui/LICENSE.txt +43 -0
- treenode/static/vendors/jquery-ui/external/jquery/jquery.js +10716 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_444444_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_555555_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_777620_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_777777_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_cc0000_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/images/ui-icons_ffffff_256x240.png +0 -0
- treenode/static/vendors/jquery-ui/index.html +297 -0
- treenode/static/vendors/jquery-ui/jquery-ui.css +438 -0
- treenode/static/vendors/jquery-ui/jquery-ui.js +5223 -0
- treenode/static/vendors/jquery-ui/jquery-ui.min.css +7 -0
- treenode/static/vendors/jquery-ui/jquery-ui.min.js +6 -0
- treenode/static/vendors/jquery-ui/jquery-ui.structure.css +16 -0
- treenode/static/vendors/jquery-ui/jquery-ui.structure.min.css +5 -0
- treenode/static/vendors/jquery-ui/jquery-ui.theme.css +439 -0
- treenode/static/vendors/jquery-ui/jquery-ui.theme.min.css +5 -0
- treenode/static/vendors/jquery-ui/package.json +82 -0
- treenode/templates/admin/treenode_changelist.html +25 -0
- treenode/templates/admin/treenode_import_export.html +85 -0
- treenode/templates/admin/treenode_rows.html +57 -0
- treenode/tests.py +3 -0
- treenode/urls.py +6 -27
- treenode/utils/__init__.py +0 -15
- treenode/utils/db/__init__.py +7 -0
- treenode/utils/db/compiler.py +114 -0
- treenode/utils/db/db_vendor.py +50 -0
- treenode/utils/db/service.py +84 -0
- treenode/utils/db/sqlcompat.py +60 -0
- treenode/utils/db/sqlquery.py +70 -0
- treenode/version.py +2 -2
- treenode/views/__init__.py +5 -0
- treenode/views/autoapi.py +91 -0
- treenode/views/autocomplete.py +52 -0
- treenode/views/children.py +41 -0
- treenode/views/common.py +23 -0
- treenode/views/crud.py +209 -0
- treenode/views/search.py +48 -0
- treenode/widgets.py +27 -44
- django_fast_treenode-2.1.4.dist-info/METADATA +0 -166
- django_fast_treenode-2.1.4.dist-info/RECORD +0 -63
- treenode/admin/mixins.py +0 -302
- treenode/managers/adjacency.py +0 -205
- treenode/managers/closure.py +0 -278
- treenode/models/adjacency.py +0 -342
- treenode/models/classproperty.py +0 -27
- treenode/models/closure.py +0 -122
- treenode/static/treenode/js/.gitkeep +0 -1
- treenode/static/treenode/js/treenode_admin.js +0 -131
- treenode/templates/admin/export_success.html +0 -26
- treenode/templates/admin/tree_node_changelist.html +0 -19
- treenode/templates/admin/tree_node_export.html +0 -27
- treenode/templates/admin/tree_node_import.html +0 -45
- treenode/templates/admin/tree_node_import_report.html +0 -32
- treenode/templates/widgets/tree_widget.css +0 -23
- treenode/utils/aid.py +0 -46
- treenode/utils/base16.py +0 -38
- treenode/utils/base36.py +0 -37
- treenode/utils/db.py +0 -116
- treenode/utils/exporter.py +0 -196
- treenode/utils/importer.py +0 -328
- treenode/utils/radix.py +0 -61
- treenode/views.py +0 -184
- {django_fast_treenode-2.1.4.dist-info → django_fast_treenode-3.0.0.dist-info/licenses}/LICENSE +0 -0
- {django_fast_treenode-2.1.4.dist-info → django_fast_treenode-3.0.0.dist-info}/top_level.txt +0 -0
- /treenode/static/{treenode → css}/.gitkeep +0 -0
- /treenode/static/{treenode/css → js}/.gitkeep +0 -0
@@ -0,0 +1,531 @@
|
|
1
|
+
/**
|
2
|
+
* treenode_admin.js
|
3
|
+
*
|
4
|
+
* Advanced TreeNode Admin extension for Django Admin.
|
5
|
+
* Adds dynamic drag-and-drop sorting, AJAX-based subtree loading,
|
6
|
+
* real-time visual feedback, and persistent tree state.
|
7
|
+
*
|
8
|
+
* Features:
|
9
|
+
* - Drag-and-drop node movement with Shift for child placement
|
10
|
+
* - Visual animations for feedback (insert flash, drag highlight)
|
11
|
+
* - Inline AJAX expansion and collapse of subtrees
|
12
|
+
* - Server-side re-rendering and recovery after node movement
|
13
|
+
* - Lightweight localStorage persistence using compressed HTML
|
14
|
+
*
|
15
|
+
* Version: 3.0.0
|
16
|
+
* Author: Timur Kady
|
17
|
+
* Email: timurkady@yandex.com
|
18
|
+
*/
|
19
|
+
|
20
|
+
|
21
|
+
(function($) {
|
22
|
+
|
23
|
+
// ------------------------------- //
|
24
|
+
// Service and auxiliary functions //
|
25
|
+
// ------------------------------- //
|
26
|
+
|
27
|
+
function debounce(func, wait) {
|
28
|
+
var timeout;
|
29
|
+
return function() {
|
30
|
+
var context = this, args = arguments;
|
31
|
+
clearTimeout(timeout);
|
32
|
+
timeout = setTimeout(function() {
|
33
|
+
func.apply(context, args);
|
34
|
+
}, wait);
|
35
|
+
};
|
36
|
+
}
|
37
|
+
|
38
|
+
function getCookie(name) {
|
39
|
+
let cookieValue = null;
|
40
|
+
if (document.cookie && document.cookie !== '') {
|
41
|
+
const cookies = document.cookie.split(';');
|
42
|
+
for (let i = 0; i < cookies.length; i++) {
|
43
|
+
const cookie = cookies[i].trim();
|
44
|
+
if (cookie.startsWith(name + '=')) {
|
45
|
+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
return cookieValue;
|
51
|
+
}
|
52
|
+
|
53
|
+
function showAdminMessage(text, level = "info", duration = 6000) {
|
54
|
+
const $ = django.jQuery || window.jQuery;
|
55
|
+
|
56
|
+
// Make sure the block exists
|
57
|
+
let msgList = $(".messagelist");
|
58
|
+
if (!msgList.length) {
|
59
|
+
msgList = $('<ul class="messagelist"></ul>');
|
60
|
+
$("#content").before(msgList);
|
61
|
+
}
|
62
|
+
|
63
|
+
// Create a message
|
64
|
+
const msg = $(`<li class="${level}">${text}</li>`);
|
65
|
+
|
66
|
+
msgList.append(msg);
|
67
|
+
|
68
|
+
// Auto disappear after 6 seconds (default)
|
69
|
+
setTimeout(() => {
|
70
|
+
msg.fadeOut(500, () => msg.remove());
|
71
|
+
}, duration);
|
72
|
+
}
|
73
|
+
|
74
|
+
function hangleAjaxSuccess(data) {
|
75
|
+
const msg = data.message || "The request was successfully completed.";
|
76
|
+
showAdminMessage(msg, "success");
|
77
|
+
ChangeList.saveTree()
|
78
|
+
ChangeList.restoreTree(ChangeList.expandedNodes);
|
79
|
+
}
|
80
|
+
|
81
|
+
function handleAjaxError(xhr, status, error) {
|
82
|
+
ChangeList.restoreTree(ChangeList.expandedNodes);
|
83
|
+
|
84
|
+
const fallbackMessage = "An unknown error occurred while executing the request.";
|
85
|
+
const $ = django.jQuery || window.jQuery;
|
86
|
+
let message = "";
|
87
|
+
|
88
|
+
// Try to extract the JSON error
|
89
|
+
try {
|
90
|
+
const contentType = xhr.getResponseHeader("Content-Type") || "";
|
91
|
+
if (contentType.includes("application/json")) {
|
92
|
+
const data = xhr.responseJSON || JSON.parse(xhr.responseText);
|
93
|
+
if (data?.error) {
|
94
|
+
message = data.error;
|
95
|
+
} else if (typeof data?.detail === "string") {
|
96
|
+
message = data.detail;
|
97
|
+
} else if (typeof data === "string") {
|
98
|
+
message = data;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
} catch (e) {
|
102
|
+
// Not JSON – silently continue
|
103
|
+
}
|
104
|
+
|
105
|
+
// Try to parse plain HTML Django error and extract exception summary
|
106
|
+
if (!message && xhr.responseText) {
|
107
|
+
try {
|
108
|
+
// Try to extract main exception message from Django debug HTML
|
109
|
+
const match = xhr.responseText.match(/<pre class="exception_value">([^<]+)<\/pre>/);
|
110
|
+
if (match && match[1]) {
|
111
|
+
message = match[1].trim();
|
112
|
+
} else {
|
113
|
+
// As fallback, extract first lines of cleaned HTML
|
114
|
+
const plain = xhr.responseText.replace(/<\/?[^>]+(>|$)/g, "").trim();
|
115
|
+
const lines = plain.split("\n").map(line => line.trim()).filter(Boolean);
|
116
|
+
if (lines.length) {
|
117
|
+
message = lines.slice(0, 3).join(" — "); // keep it brief
|
118
|
+
}
|
119
|
+
}
|
120
|
+
} catch (e) {
|
121
|
+
// Parsing failed, do nothing
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
// If there is still nothing, build a generic fallback
|
126
|
+
if (!message) {
|
127
|
+
if (xhr.status) {
|
128
|
+
message = `${xhr.status}: ${error || status}`;
|
129
|
+
} else {
|
130
|
+
message = fallbackMessage;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
showAdminMessage(message, "error");
|
135
|
+
}
|
136
|
+
|
137
|
+
function isDarkTheme() {
|
138
|
+
return document.documentElement.getAttribute("data-theme") === "dark";
|
139
|
+
}
|
140
|
+
|
141
|
+
function applyTheme() {
|
142
|
+
var dark = isDarkTheme();
|
143
|
+
var $container = $(".tree-widget");
|
144
|
+
var $dropdown = $(".tree-widget-dropdown");
|
145
|
+
|
146
|
+
if (dark) {
|
147
|
+
$dropdown.addClass("dark-theme");
|
148
|
+
$container.addClass("dark-theme");
|
149
|
+
} else {
|
150
|
+
$dropdown.removeClass("dark-theme");
|
151
|
+
$container.removeClass("dark-theme");
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
let ajaxCounter = 0;
|
156
|
+
|
157
|
+
function clearCursor() {
|
158
|
+
ajaxCounter--;
|
159
|
+
if (ajaxCounter === 0) {
|
160
|
+
$('body').css('cursor', 'default');
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
// ------------------------------- //
|
165
|
+
// AJAX Setup //
|
166
|
+
// ------------------------------- //
|
167
|
+
|
168
|
+
const csrftoken = getCookie('csrftoken');
|
169
|
+
|
170
|
+
$.ajaxSetup({
|
171
|
+
beforeSend: function(xhr, settings) {
|
172
|
+
if (!/^https?:.*/.test(settings.url)) {
|
173
|
+
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
174
|
+
}
|
175
|
+
ajaxCounter++;
|
176
|
+
$('body').css('cursor', 'wait');
|
177
|
+
},
|
178
|
+
cache: false
|
179
|
+
});
|
180
|
+
|
181
|
+
// ------------------------------- //
|
182
|
+
// Animations and visual feedback //
|
183
|
+
// ------------------------------- //
|
184
|
+
|
185
|
+
const TreeFx = {
|
186
|
+
// Highlight the line where the element was moved
|
187
|
+
flashInsert(nodeId) {
|
188
|
+
const $row = $(`tr[data-node-id="${nodeId}"]`);
|
189
|
+
if (!$row.length) return;
|
190
|
+
|
191
|
+
$row.addClass("flash-insert");
|
192
|
+
setTimeout(() => $row.removeClass("flash-insert"), 1000);
|
193
|
+
},
|
194
|
+
|
195
|
+
// Fading in a new tbody (e.g. after reloadTree)
|
196
|
+
fadeInTbody(newHTML, callback) {
|
197
|
+
const $tbody = $('table#result_list tbody');
|
198
|
+
$tbody.stop(true, true).fadeOut(100, () => {
|
199
|
+
$tbody.html(newHTML).fadeIn(150, callback);
|
200
|
+
});
|
201
|
+
},
|
202
|
+
|
203
|
+
// Visual cue when drag starts and ends
|
204
|
+
markDragging($item, enable) {
|
205
|
+
$item.toggleClass('dragging', enable);
|
206
|
+
},
|
207
|
+
};
|
208
|
+
|
209
|
+
|
210
|
+
// ------------------------------- //
|
211
|
+
// Main Class //
|
212
|
+
// ------------------------------- //
|
213
|
+
|
214
|
+
|
215
|
+
var ChangeList = {
|
216
|
+
$tableBody: null,
|
217
|
+
isShiftPressed: false,
|
218
|
+
activeTargetRow: null,
|
219
|
+
isMoving: false,
|
220
|
+
expandedNodes: [],
|
221
|
+
label: '',
|
222
|
+
|
223
|
+
init: function() {
|
224
|
+
this.$tableBody = $('table#result_list tbody');
|
225
|
+
this.bindEvents();
|
226
|
+
this.restoreTree();
|
227
|
+
this.enableDragAndDrop();
|
228
|
+
},
|
229
|
+
|
230
|
+
saveTree: function() {
|
231
|
+
if (!this.$tableBody) return;
|
232
|
+
|
233
|
+
this.expandedNodes = [];
|
234
|
+
this.$tableBody.find(".treenode-toggle").each(function () {
|
235
|
+
$btn = $(this)
|
236
|
+
if ($btn.data('expanded')) {
|
237
|
+
ChangeList.expandedNodes.push($btn.data('node-id'));
|
238
|
+
}
|
239
|
+
});
|
240
|
+
if (!(this.expandedNodes ) || (this.expandedNodes.length === 0)) {
|
241
|
+
localStorage.removeItem("saved_tbody");
|
242
|
+
} else {
|
243
|
+
localStorage.setItem("saved_tbody", JSON.stringify(this.expandedNodes));
|
244
|
+
}
|
245
|
+
const count = $("#result_list tbody tr").length;
|
246
|
+
if (count > 0) {
|
247
|
+
$("p.paginator").first().text(`${count} ${ChangeList.label}`);
|
248
|
+
localStorage.setItem("label", ChangeList.label);
|
249
|
+
}
|
250
|
+
|
251
|
+
},
|
252
|
+
|
253
|
+
restoreTree: function(expandedList = null) {
|
254
|
+
const expanded = expandedList || JSON.parse(localStorage.getItem("saved_tbody") || "[]");
|
255
|
+
ChangeList.label = localStorage.getItem("label") || "";
|
256
|
+
if (!expanded.length) return;
|
257
|
+
|
258
|
+
const params = { expanded: JSON.stringify(expanded) };
|
259
|
+
|
260
|
+
$.ajax({
|
261
|
+
url: 'change_list/',
|
262
|
+
method: 'GET',
|
263
|
+
data: params,
|
264
|
+
dataType: 'json',
|
265
|
+
success: function(data) {
|
266
|
+
ChangeList.$tableBody.html(data.html);
|
267
|
+
ChangeList.expandedNodes = expanded;
|
268
|
+
|
269
|
+
ChangeList.$tableBody.find(".treenode-toggle").each(function () {
|
270
|
+
const $btn = $(this);
|
271
|
+
const nodeId = $btn.data('node-id');
|
272
|
+
if (ChangeList.expandedNodes.includes(nodeId)) {
|
273
|
+
$btn.data("expanded", true);
|
274
|
+
$btn.text("▼");
|
275
|
+
}
|
276
|
+
});
|
277
|
+
|
278
|
+
if (data.label) ChangeList.label = data.label;
|
279
|
+
const count = $("#result_list tbody tr").length;
|
280
|
+
$("p.paginator").first().text(`${count} ${ChangeList.label}`);
|
281
|
+
},
|
282
|
+
error: function(xhr, status, error) {
|
283
|
+
handleAjaxError(xhr, status, error);
|
284
|
+
// console.error("Restore error:", status, error);
|
285
|
+
},
|
286
|
+
complete: function() {
|
287
|
+
clearCursor();
|
288
|
+
}
|
289
|
+
});
|
290
|
+
},
|
291
|
+
|
292
|
+
searchRows: function(searchQuery) {
|
293
|
+
var params = { q: searchQuery };
|
294
|
+
|
295
|
+
$.ajax({
|
296
|
+
url: 'change_list/',
|
297
|
+
method: 'GET',
|
298
|
+
data: params,
|
299
|
+
dataType: 'json',
|
300
|
+
success: function(data) {
|
301
|
+
ChangeList.$tableBody.html(data.html);
|
302
|
+
},
|
303
|
+
error: function(xhr, status, error) {
|
304
|
+
handleAjaxError(xhr, status, error);
|
305
|
+
// console.error("Search error:", status, error);
|
306
|
+
},
|
307
|
+
complete: function() {
|
308
|
+
clearCursor();
|
309
|
+
}
|
310
|
+
});
|
311
|
+
},
|
312
|
+
|
313
|
+
insertRows: function($parentRow, parentId) {
|
314
|
+
var parent_id = parentId
|
315
|
+
var params = {parent_id: parent_id};
|
316
|
+
|
317
|
+
$.ajax({
|
318
|
+
url: 'change_list/',
|
319
|
+
method: 'GET',
|
320
|
+
data: params,
|
321
|
+
dataType: 'json',
|
322
|
+
success: function(data) {
|
323
|
+
$parentRow.after(data.html);
|
324
|
+
if (data.label) ChangeList.label = data.label;
|
325
|
+
},
|
326
|
+
error: function(xhr, status, error) {
|
327
|
+
handleAjaxError(xhr, status, error);
|
328
|
+
// console.error("Insert rows error:", status, error);
|
329
|
+
},
|
330
|
+
complete: function() {
|
331
|
+
ChangeList.saveTree();
|
332
|
+
clearCursor();
|
333
|
+
}
|
334
|
+
});
|
335
|
+
},
|
336
|
+
|
337
|
+
removeRows: function(parentId) {
|
338
|
+
var $children = this.$tableBody.find(`tr[data-parent-of="${parentId}"]`);
|
339
|
+
$children.each(function () {
|
340
|
+
var childId = $(this).data('node-id');
|
341
|
+
ChangeList.removeRows(childId);
|
342
|
+
});
|
343
|
+
$children.remove();
|
344
|
+
this.saveTree();
|
345
|
+
},
|
346
|
+
|
347
|
+
toggleNode: function($btn) {
|
348
|
+
var expanded = $btn.data('expanded');
|
349
|
+
var $parentRow = $btn.closest('tr');
|
350
|
+
var parentId = $btn.data('node-id');
|
351
|
+
|
352
|
+
if (expanded) {
|
353
|
+
$btn.html('►').data('expanded', false);
|
354
|
+
this.removeRows(parentId);
|
355
|
+
} else {
|
356
|
+
$btn.html('▼').data('expanded', true);
|
357
|
+
this.insertRows($parentRow, parentId);
|
358
|
+
}
|
359
|
+
},
|
360
|
+
|
361
|
+
bindEvents: function() {
|
362
|
+
var self = this;
|
363
|
+
|
364
|
+
// Search listener
|
365
|
+
$('input[name="q"]')
|
366
|
+
.on("focus", function() {
|
367
|
+
self.saveTree();
|
368
|
+
})
|
369
|
+
.on("keyup", debounce(function() {
|
370
|
+
var query = $.trim($(this).val());
|
371
|
+
if (query === '') {
|
372
|
+
self.restoreTree(ChangeList.expandedNodes);
|
373
|
+
} else {
|
374
|
+
self.searchRows(query);
|
375
|
+
}
|
376
|
+
}, 300));
|
377
|
+
|
378
|
+
// Toggle buttons listener
|
379
|
+
$(document).on("click", "button.treenode-toggle", function(e) {
|
380
|
+
e.preventDefault();
|
381
|
+
var $btn = $(this);
|
382
|
+
self.toggleNode($btn);
|
383
|
+
});
|
384
|
+
|
385
|
+
// Shift key hold listener
|
386
|
+
$(document).on("keydown", function(e) {
|
387
|
+
if (e.key === "Shift") {
|
388
|
+
ChangeList.isShiftPressed = true;
|
389
|
+
ChangeList.updateDndHighlight();
|
390
|
+
}
|
391
|
+
})
|
392
|
+
.on("keyup", function(e) {
|
393
|
+
if (e.key === "Shift") {
|
394
|
+
ChangeList.isShiftPressed = false;
|
395
|
+
ChangeList.updateDndHighlight();
|
396
|
+
}
|
397
|
+
});
|
398
|
+
|
399
|
+
// Listener for admin panel theme change button
|
400
|
+
$(document).on("click", "button.theme-toggle", function() {
|
401
|
+
applyTheme();
|
402
|
+
});
|
403
|
+
|
404
|
+
// Fix action selets
|
405
|
+
$('#result_list').on('change', '#action-toggle', function() {
|
406
|
+
$('input.action-select').prop('checked', this.checked);
|
407
|
+
});
|
408
|
+
},
|
409
|
+
|
410
|
+
enableDragAndDrop: function() {
|
411
|
+
const self = this;
|
412
|
+
|
413
|
+
this.$tableBody.sortable({
|
414
|
+
items: "tr",
|
415
|
+
handle: ".treenode-drag-handle",
|
416
|
+
placeholder: "treenode-placeholder",
|
417
|
+
activeTargetRow: null,
|
418
|
+
helper: function(e, tr) {
|
419
|
+
const $originals = tr.children();
|
420
|
+
const $helper = tr.clone();
|
421
|
+
$helper.find('[id]').each(function() {
|
422
|
+
$(this).removeAttr('id');
|
423
|
+
});
|
424
|
+
$helper.children().each(function(index) {
|
425
|
+
$(this).width($originals.eq(index).width());
|
426
|
+
});
|
427
|
+
return $helper;
|
428
|
+
},
|
429
|
+
start: function(e, ui) {
|
430
|
+
TreeFx.markDragging(ui.item, true);
|
431
|
+
// ChangeList.updateDndHighlight();
|
432
|
+
// console.log("Drag started:", ui.item.data("node-id"));
|
433
|
+
},
|
434
|
+
over: function(e, ui) {
|
435
|
+
ChangeList.updateDndHighlight();
|
436
|
+
// console.log("over")
|
437
|
+
},
|
438
|
+
stop: function(e, ui) {
|
439
|
+
TreeFx.markDragging(ui.item, false);
|
440
|
+
|
441
|
+
const $item = ui.item;
|
442
|
+
const nodeId = $item.data("node-id");
|
443
|
+
const prevId = $item.prev().data("node-id") || null;
|
444
|
+
const nextId = $item.next().data("node-id") || null;
|
445
|
+
const isChild = ChangeList.isShiftPressed;
|
446
|
+
|
447
|
+
if (ChangeList.activeTargetRow) {
|
448
|
+
ChangeList.activeTargetRow.removeClass("target-as-child");
|
449
|
+
ChangeList.activeTargetRow = null;
|
450
|
+
}
|
451
|
+
|
452
|
+
mode = isChild ? 'child' : 'after';
|
453
|
+
ChangeList.applyMove(nodeId, prevId, mode)
|
454
|
+
|
455
|
+
TreeFx.flashInsert(nodeId);
|
456
|
+
},
|
457
|
+
});
|
458
|
+
},
|
459
|
+
|
460
|
+
updateDndHighlight: function() {
|
461
|
+
const $placeholder = this.$tableBody.find("tr.treenode-placeholder");
|
462
|
+
const $target = $placeholder.prev();
|
463
|
+
|
464
|
+
if (!($target && $target.length && $target.data("node-id"))) {
|
465
|
+
this.$tableBody.find("tr.target-as-child").removeClass("target-as-child");
|
466
|
+
this.activeTargetRow = null;
|
467
|
+
return;
|
468
|
+
}
|
469
|
+
|
470
|
+
if (this.isShiftPressed) {
|
471
|
+
if (!this.activeTargetRow || !this.activeTargetRow.is($target)) {
|
472
|
+
this.$tableBody.find("tr.target-as-child").removeClass("target-as-child");
|
473
|
+
$target.addClass("target-as-child");
|
474
|
+
this.activeTargetRow = $target;
|
475
|
+
}
|
476
|
+
} else {
|
477
|
+
if (this.activeTargetRow) {
|
478
|
+
this.activeTargetRow.removeClass("target-as-child");
|
479
|
+
this.activeTargetRow = null;
|
480
|
+
}
|
481
|
+
}
|
482
|
+
},
|
483
|
+
|
484
|
+
applyMove: function(nodeId, targetId, mode) {
|
485
|
+
if (this.isMoving) return;
|
486
|
+
|
487
|
+
this.isMoving = true;
|
488
|
+
this.activeTargetRow = null;
|
489
|
+
|
490
|
+
const params = {
|
491
|
+
node_id: nodeId,
|
492
|
+
target_id: targetId,
|
493
|
+
mode: mode,
|
494
|
+
expanded: JSON.stringify(this.expandedNodes)
|
495
|
+
};
|
496
|
+
|
497
|
+
// console.log(params);
|
498
|
+
|
499
|
+
$.ajax({
|
500
|
+
url: 'move/',
|
501
|
+
method: 'POST',
|
502
|
+
data: params,
|
503
|
+
dataType: 'json',
|
504
|
+
success: function(data) {
|
505
|
+
hangleAjaxSuccess(data);
|
506
|
+
},
|
507
|
+
error: function(xhr, status, error) {
|
508
|
+
handleAjaxError(xhr, status, error);
|
509
|
+
},
|
510
|
+
complete: function() {
|
511
|
+
ChangeList.isMoving = false;
|
512
|
+
clearCursor();
|
513
|
+
}
|
514
|
+
});
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
// ------------------------------- //
|
519
|
+
// Init //
|
520
|
+
// ------------------------------- //
|
521
|
+
|
522
|
+
$(document).ready(function () {
|
523
|
+
document.body.style.cursor = '';
|
524
|
+
applyTheme();
|
525
|
+
if ($("table#result_list").length) {
|
526
|
+
ChangeList.init();
|
527
|
+
}
|
528
|
+
});
|
529
|
+
|
530
|
+
})(django.jQuery || window.jQuery);
|
531
|
+
|