Translated using Weblate (Sinhala)
[phpmyadmin.git] / js / src / jquery.sortable-table.js
blob230565ed5e0b6f848a1c92728560794a7dc97465
1 /**
2  * This file is internal to phpMyAdmin.
3  * @license see the main phpMyAdmin license.
4  *
5  * @fileoverview    A jquery plugin that allows drag&drop sorting in tables.
6  *                  Coded because JQuery UI sortable doesn't support tables. Also it has no animation
7  *
8  * @name            Sortable Table JQuery plugin
9  *
10  * @requires        jQuery
11  */
13 /**
14  * Options:
15  *
16  * $('table').sortableTable({
17  *   ignoreRect: { top, left, width, height } - Relative coordinates on each element. If the user clicks
18  *                                              in this area, it is not seen as a drag&drop request. Useful for toolbars etc.
19  *   events: {
20  *     start: callback function when the user starts dragging
21  *     drop: callback function after an element has been dropped
22  *   }
23  * })
24  */
26 /**
27  * Commands:
28  *
29  * $('table').sortableTable('init')    - equivalent to $('table').sortableTable()
30  * $('table').sortableTable('refresh') - if the table has been changed, refresh correctly assigns all events again
31  * $('table').sortableTable('destroy') - removes all events from the table
32  */
34 /**
35  * Setup:
36  *
37  * Can be applied on any table, there is just one convention.
38  * Each cell (<td>) has to contain one and only one element (preferably div or span)
39  * which is the actually draggable element.
40  */
41 (function ($) {
42     jQuery.fn.sortableTable = function (method) {
43         var methods = {
44             init: function (options) {
45                 var tb = new SortableTableInstance(this, options);
46                 tb.init();
47                 $(this).data('sortableTable', tb);
48             },
49             refresh: function () {
50                 $(this).data('sortableTable').refresh();
51             },
52             destroy: function () {
53                 $(this).data('sortableTable').destroy();
54             }
55         };
57         if (methods[method]) {
58             return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
59         } else if (typeof method === 'object' || !method) {
60             return methods.init.apply(this, arguments);
61         } else {
62             $.error('Method ' + method + ' does not exist on jQuery.sortableTable');
63         }
65         function SortableTableInstance (table, options = {}) {
66             var down = false;
67             var $draggedEl;
68             var oldCell;
69             var previewMove;
70             var id;
72             /* Mouse handlers on the child elements */
73             var onMouseUp = function (e) {
74                 dropAt(e.pageX, e.pageY);
75             };
77             var onMouseDown = function (e) {
78                 $draggedEl = $(this).children();
79                 if ($draggedEl.length === 0) {
80                     return;
81                 }
82                 if (options.ignoreRect && insideRect({ x: e.pageX - $draggedEl.offset().left, y: e.pageY - $draggedEl.offset().top }, options.ignoreRect)) {
83                     return;
84                 }
86                 down = true;
87                 oldCell = this;
89                 if (options.events && options.events.start) {
90                     options.events.start(this);
91                 }
93                 return false;
94             };
96             var globalMouseMove = function (e) {
97                 if (down) {
98                     move(e.pageX, e.pageY);
100                     if (inside($(oldCell), e.pageX, e.pageY)) {
101                         if (previewMove !== null) {
102                             moveTo(previewMove);
103                             previewMove = null;
104                         }
105                     } else {
106                         $(table).find('td').each(function () {
107                             if (inside($(this), e.pageX, e.pageY)) {
108                                 if ($(previewMove).attr('class') !== $(this).children().first().attr('class')) {
109                                     if (previewMove !== null) {
110                                         moveTo(previewMove);
111                                     }
112                                     previewMove = $(this).children().first();
113                                     if (previewMove.length > 0) {
114                                         moveTo($(previewMove), {
115                                             pos: {
116                                                 top: $(oldCell).offset().top - $(previewMove).parent().offset().top,
117                                                 left: $(oldCell).offset().left - $(previewMove).parent().offset().left
118                                             }
119                                         });
120                                     }
121                                 }
123                                 return false;
124                             }
125                         });
126                     }
127                 }
129                 return false;
130             };
132             var globalMouseOut = function () {
133                 if (down) {
134                     down = false;
135                     if (previewMove) {
136                         moveTo(previewMove);
137                     }
138                     moveTo($draggedEl);
139                     previewMove = null;
140                 }
141             };
143             // Initialize sortable table
144             this.init = function () {
145                 id = 1;
146                 // Add some required css to each child element in the <td>s
147                 $(table).find('td').children().each(function () {
148                     // Remove any old occurrences of our added draggable-num class
149                     $(this).attr('class', $(this).attr('class').replace(/\s*draggable-\d+/g, ''));
150                     $(this).addClass('draggable-' + (id++));
151                 });
153                 // Mouse events
154                 $(table).find('td').on('mouseup', onMouseUp);
155                 $(table).find('td').on('mousedown', onMouseDown);
157                 $(document).on('mousemove', globalMouseMove);
158                 $(document).on('mouseleave', globalMouseOut);
159             };
161             // Call this when the table has been updated
162             this.refresh = function () {
163                 this.destroy();
164                 this.init();
165             };
167             this.destroy = function () {
168                 // Add some required css to each child element in the <td>s
169                 $(table).find('td').children().each(function () {
170                     // Remove any old occurrences of our added draggable-num class
171                     $(this).attr('class', $(this).attr('class').replace(/\s*draggable-\d+/g, ''));
172                 });
174                 // Mouse events
175                 $(table).find('td').off('mouseup', onMouseUp);
176                 $(table).find('td').off('mousedown', onMouseDown);
178                 $(document).off('mousemove', globalMouseMove);
179                 $(document).off('mouseleave', globalMouseOut);
180             };
182             function switchElement (drag, dropTo) {
183                 var dragPosDiff = {
184                     left: $(drag).children().first().offset().left - $(dropTo).offset().left,
185                     top: $(drag).children().first().offset().top - $(dropTo).offset().top
186                 };
188                 var dropPosDiff = null;
189                 if ($(dropTo).children().length > 0) {
190                     dropPosDiff = {
191                         left: $(dropTo).children().first().offset().left - $(drag).offset().left,
192                         top: $(dropTo).children().first().offset().top - $(drag).offset().top
193                     };
194                 }
196                 /* I love you append(). It moves the DOM Elements so gracefully <3 */
197                 // Put the element in the way to old place
198                 $(drag).append($(dropTo).children().first()).children()
199                     .stop(true, true)
200                     .on('mouseup', onMouseUp);
202                 if (dropPosDiff) {
203                     $(drag).append($(dropTo).children().first()).children()
204                         .css('left', dropPosDiff.left + 'px')
205                         .css('top', dropPosDiff.top + 'px');
206                 }
208                 // Put our dragged element into the space we just freed up
209                 $(dropTo).append($(drag).children().first()).children()
210                     .on('mouseup', onMouseUp)
211                     .css('left', dragPosDiff.left + 'px')
212                     .css('top', dragPosDiff.top + 'px');
214                 moveTo($(dropTo).children().first(), { duration: 100 });
215                 moveTo($(drag).children().first(), { duration: 100 });
217                 if (options.events && options.events.drop) {
218                     // Drop event. The drag child element is moved into the drop element
219                     // and vice versa. So the parameters are switched.
221                     // Calculate row and column index
222                     const colIdx = $(dropTo).prevAll().length;
223                     const rowIdx = $(dropTo).parent().prevAll().length;
225                     options.events.drop(drag, dropTo, { col: colIdx, row: rowIdx });
226                 }
227             }
229             function move (x, y) {
230                 $draggedEl.offset({
231                     top: Math.min($(document).height(), Math.max(0, y - $draggedEl.height() / 2)),
232                     left: Math.min($(document).width(), Math.max(0, x - $draggedEl.width() / 2))
233                 });
234             }
236             function inside ($el, x, y) {
237                 var off = $el.offset();
238                 return y >= off.top && x >= off.left && x < off.left + $el.width() && y < off.top + $el.height();
239             }
241             function insideRect (pos, r) {
242                 return pos.y > r.top && pos.x > r.left && pos.y < r.top + r.height && pos.x < r.left + r.width;
243             }
245             function dropAt (x, y) {
246                 if (!down) {
247                     return;
248                 }
249                 down = false;
251                 var switched = false;
253                 $(table).find('td').each(function () {
254                     if ($(this).children().first().attr('class') !== $(oldCell).children().first().attr('class') && inside($(this), x, y)) {
255                         switchElement(oldCell, this);
256                         switched = true;
257                     }
258                 });
260                 if (!switched) {
261                     if (previewMove) {
262                         moveTo(previewMove);
263                     }
264                     moveTo($draggedEl);
265                 }
267                 previewMove = null;
268             }
270             function moveTo (elem, opts = {}) {
271                 if (!opts.pos) {
272                     opts.pos = { left: 0, top: 0 };
273                 }
274                 if (!opts.duration) {
275                     opts.duration = 200;
276                 }
278                 $(elem).css('position', 'relative');
279                 $(elem).animate({ top: opts.pos.top, left: opts.pos.left }, {
280                     duration: opts.duration,
281                     complete: function () {
282                         if (opts.pos.left === 0 && opts.pos.top === 0) {
283                             $(elem)
284                                 .css('position', '')
285                                 .css('left', '')
286                                 .css('top', '');
287                         }
288                     }
289                 });
290             }
291         }
292     };
293 }(jQuery));