1
- 'use strict' ;
2
-
3
1
/*
4
2
jQuery UI Sortable plugin wrapper
5
3
6
4
@param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
7
- */
5
+ */
8
6
angular . module ( 'ui.sortable' , [ ] )
9
- . value ( 'uiSortableConfig' , { } )
10
- . directive ( 'uiSortable' , [ 'uiSortableConfig' , '$log' ,
11
- function ( uiSortableConfig , log ) {
7
+ . value ( 'uiSortableConfig' , { } )
8
+ . directive ( 'uiSortable' , [
9
+ 'uiSortableConfig' , '$timeout' , '$log' ,
10
+ function ( uiSortableConfig , $timeout , $log ) {
12
11
return {
13
12
require : '?ngModel' ,
14
13
link : function ( scope , element , attrs , ngModel ) {
14
+ var savedNodes ;
15
15
16
- function combineCallbacks ( first , second ) {
17
- if ( second && ( typeof second === ' function' ) ) {
16
+ function combineCallbacks ( first , second ) {
17
+ if ( second && ( typeof second === " function" ) ) {
18
18
return function ( e , ui ) {
19
19
first ( e , ui ) ;
20
20
second ( e , ui ) ;
@@ -27,100 +27,140 @@ angular.module('ui.sortable', [])
27
27
28
28
var callbacks = {
29
29
receive : null ,
30
- remove : null ,
31
- start : null ,
32
- stop : null ,
33
- update : null
34
- } ;
35
-
36
- var apply = function ( e , ui ) {
37
- if ( ui . item . sortable . resort || ui . item . sortable . relocate ) {
38
- scope . $apply ( ) ;
39
- }
30
+ remove :null ,
31
+ start :null ,
32
+ stop :null ,
33
+ update :null
40
34
} ;
41
35
42
36
angular . extend ( opts , uiSortableConfig ) ;
43
37
44
38
if ( ngModel ) {
45
39
46
- ngModel . $render = function ( ) {
47
- element . sortable ( 'refresh' ) ;
48
- } ;
40
+ // When we add or remove elements, we need the sortable to 'refresh'
41
+ // so it can find the new/removed elements.
42
+ scope . $watch ( attrs . ngModel + '.length' , function ( ) {
43
+ // Timeout to let ng-repeat modify the DOM
44
+ $timeout ( function ( ) {
45
+ element . sortable ( "refresh" ) ;
46
+ } ) ;
47
+ } ) ;
49
48
50
49
callbacks . start = function ( e , ui ) {
51
- // Save position of dragged item
52
- ui . item . sortable = {
53
- index : ui . item . index ( )
54
- } ;
50
+ // Save the starting position of dragged item
51
+ ui . item . sortable = { index : ui . item . index ( ) } ;
55
52
} ;
56
53
57
- callbacks . update = function ( e , ui ) {
58
- // For some reason the reference to ngModel in stop() is wrong
59
- ui . item . sortable . resort = ngModel ;
54
+ callbacks . activate = function ( e , ui ) {
55
+ // We need to make a copy of the current element's contents so
56
+ // we can restore it after sortable has messed it up.
57
+ // This is inside activate (instead of start) in order to save
58
+ // both lists when dragging between connected lists.
59
+ savedNodes = element . contents ( ) ;
60
+
61
+ // If this list has a placeholder (the connected lists won't),
62
+ // don't inlcude it in saved nodes.
63
+ var placeholder = element . sortable ( 'option' , 'placeholder' ) ;
64
+
65
+ // placeholder.element will be a function if the placeholder, has
66
+ // been created (placeholder will be an object). If it hasn't
67
+ // been created, either placeholder will be false if no
68
+ // placeholder class was given or placeholder.element will be
69
+ // undefined if a class was given (placeholder will be a string)
70
+ if ( placeholder && placeholder . element ) {
71
+ savedNodes = savedNodes . not ( element . find (
72
+ "." + placeholder . element ( )
73
+ . attr ( 'class' ) . split ( / \s + / ) . join ( '.' ) ) ) ;
74
+ }
60
75
} ;
61
76
62
- callbacks . receive = function ( e , ui ) {
63
- ui . item . sortable . relocate = true ;
64
- // added item to array into correct position and set up flag
65
- ngModel . $modelValue . splice ( ui . item . index ( ) , 0 , ui . item . sortable . moved ) ;
66
- } ;
77
+ callbacks . update = function ( e , ui ) {
78
+ // Save current drop position but only if this is not a second
79
+ // update that happens when moving between lists because then
80
+ // the value will be overwritten with the old value
81
+ if ( ! ui . item . sortable . received ) {
82
+ ui . item . sortable . dropindex = ui . item . index ( ) ;
83
+
84
+ // Cancel the sort (let ng-repeat do the sort for us)
85
+ // Don't cancel if this is the received list because it has
86
+ // already been canceled in the other list, and trying to cancel
87
+ // here will mess up the DOM.
88
+ element . sortable ( 'cancel' ) ;
89
+ }
67
90
68
- callbacks . remove = function ( e , ui ) {
69
- // copy data into item
70
- if ( ngModel . $modelValue . length === 1 ) {
71
- ui . item . sortable . moved = ngModel . $modelValue . splice ( 0 , 1 ) [ 0 ] ;
72
- } else {
73
- ui . item . sortable . moved = ngModel . $modelValue . splice ( ui . item . sortable . index , 1 ) [ 0 ] ;
91
+ // Put the nodes back exactly the way they started (this is very
92
+ // important because ng-repeat uses comment elements to delineate
93
+ // the start and stop of repeat sections and sortable doesn't
94
+ // respect their order (even if we cancel, the order of the
95
+ // comments are still messed up).
96
+ savedNodes . detach ( ) . appendTo ( element ) ;
97
+
98
+ // If received is true (an item was dropped in from another list)
99
+ // then we add the new item to this list otherwise wait until the
100
+ // stop event where we will know if it was a sort or item was
101
+ // moved here from another list
102
+ if ( ui . item . sortable . received ) {
103
+ scope . $apply ( function ( ) {
104
+ ngModel . $modelValue . splice ( ui . item . sortable . dropindex , 0 ,
105
+ ui . item . sortable . moved ) ;
106
+ } ) ;
74
107
}
75
108
} ;
76
109
77
110
callbacks . stop = function ( e , ui ) {
78
- // digest all prepared changes
79
- if ( ui . item . sortable . resort && ! ui . item . sortable . relocate ) {
80
-
81
- // Fetch saved and current position of dropped element
82
- var end , start ;
83
- start = ui . item . sortable . index ;
84
- end = ui . item . index ( ) ;
85
-
86
- // Reorder array and apply change to scope
87
- ui . item . sortable . resort . $modelValue . splice ( end , 0 , ui . item . sortable . resort . $modelValue . splice ( start , 1 ) [ 0 ] ) ;
88
-
111
+ // If the received flag hasn't be set on the item, this is a
112
+ // normal sort, if dropindex is set, the item was moved, so move
113
+ // the items in the list.
114
+ if ( ! ui . item . sortable . received && ( 'dropindex' in ui . item . sortable ) ) {
115
+ scope . $apply ( function ( ) {
116
+ ngModel . $modelValue . splice (
117
+ ui . item . sortable . dropindex , 0 ,
118
+ ngModel . $modelValue . splice ( ui . item . sortable . index , 1 ) [ 0 ] ) ;
119
+ } ) ;
89
120
}
90
121
} ;
91
122
92
- scope . $watch ( attrs . uiSortable , function ( newVal ) {
93
- angular . forEach ( newVal , function ( value , key ) {
123
+ callbacks . receive = function ( e , ui ) {
124
+ // An item was dropped here from another list, set a flag on the
125
+ // item.
126
+ ui . item . sortable . received = true ;
127
+ } ;
94
128
95
- if ( callbacks [ key ] ) {
96
- // wrap the callback
97
- value = combineCallbacks ( callbacks [ key ] , value ) ;
129
+ callbacks . remove = function ( e , ui ) {
130
+ // Remove the item from this list's model and copy data into item,
131
+ // so the next list can retrive it
132
+ scope . $apply ( function ( ) {
133
+ ui . item . sortable . moved = ngModel . $modelValue . splice (
134
+ ui . item . sortable . index , 1 ) [ 0 ] ;
135
+ } ) ;
136
+ } ;
98
137
99
- if ( key === 'stop' ) {
138
+ scope . $watch ( attrs . uiSortable , function ( newVal , oldVal ) {
139
+ angular . forEach ( newVal , function ( value , key ) {
140
+ if ( callbacks [ key ] ) {
141
+ if ( key === 'stop' ) {
100
142
// call apply after stop
101
- value = combineCallbacks ( value , apply ) ;
143
+ value = combineCallbacks (
144
+ value , function ( ) { scope . $apply ( ) ; } ) ;
102
145
}
146
+ // wrap the callback
147
+ value = combineCallbacks ( callbacks [ key ] , value ) ;
103
148
}
104
-
105
149
element . sortable ( 'option' , key , value ) ;
106
150
} ) ;
107
151
} , true ) ;
108
152
109
153
angular . forEach ( callbacks , function ( value , key ) {
110
-
111
154
opts [ key ] = combineCallbacks ( value , opts [ key ] ) ;
112
155
} ) ;
113
156
114
- // call apply after stop
115
- opts . stop = combineCallbacks ( opts . stop , apply ) ;
116
-
117
157
} else {
118
- log . info ( 'ui.sortable: ngModel not provided!' , element ) ;
158
+ $ log. info ( 'ui.sortable: ngModel not provided!' , element ) ;
119
159
}
120
160
121
161
// Create sortable
122
162
element . sortable ( opts ) ;
123
163
}
124
164
} ;
125
165
}
126
- ] ) ;
166
+ ] ) ;
0 commit comments