1
+ ( function ( window , angular , undefined ) { 'use strict' ;
1
2
/*
2
3
jQuery UI Sortable plugin wrapper
3
4
4
5
@param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
5
6
*/
6
- angular . module ( 'ui.sortable' , [ ] ) . value ( 'uiSortableConfig' , { } ) . directive ( 'uiSortable' , [
7
- 'uiSortableConfig' ,
8
- '$timeout' ,
9
- '$log' ,
10
- function ( uiSortableConfig , $timeout , $log ) {
11
- 'use strict' ;
12
- return {
13
- require : '?ngModel' ,
14
- link : function ( scope , element , attrs , ngModel ) {
15
- var savedNodes ;
16
- function combineCallbacks ( first , second ) {
17
- if ( second && typeof second === 'function' ) {
18
- return function ( e , ui ) {
19
- first ( e , ui ) ;
20
- second ( e , ui ) ;
21
- } ;
7
+ angular . module ( 'ui.sortable' , [ ] )
8
+ . value ( 'uiSortableConfig' , { } )
9
+ . directive ( 'uiSortable' , [
10
+ 'uiSortableConfig' , '$timeout' , '$log' ,
11
+ function ( uiSortableConfig , $timeout , $log ) {
12
+ 'use strict' ;
13
+
14
+ return {
15
+ require : '?ngModel' ,
16
+ link : function ( scope , element , attrs , ngModel ) {
17
+ var savedNodes ;
18
+
19
+ function combineCallbacks ( first , second ) {
20
+ if ( second && ( typeof second === 'function' ) ) {
21
+ return function ( e , ui ) {
22
+ first ( e , ui ) ;
23
+ second ( e , ui ) ;
24
+ } ;
25
+ }
26
+ return first ;
22
27
}
23
- return first ;
24
- }
25
- var opts = { } ;
26
- var callbacks = {
28
+
29
+ var opts = { } ;
30
+
31
+ var callbacks = {
27
32
receive : null ,
28
- remove : null ,
29
- start : null ,
30
- stop : null ,
31
- update : null
33
+ remove :null ,
34
+ start :null ,
35
+ stop :null ,
36
+ update :null
32
37
} ;
33
- angular . extend ( opts , uiSortableConfig ) ;
34
- if ( ngModel ) {
35
- // When we add or remove elements, we need the sortable to 'refresh'
36
- // so it can find the new/removed elements.
37
- scope . $watch ( attrs . ngModel + '.length' , function ( ) {
38
- // Timeout to let ng-repeat modify the DOM
39
- $timeout ( function ( ) {
40
- element . sortable ( 'refresh' ) ;
38
+
39
+ angular . extend ( opts , uiSortableConfig ) ;
40
+
41
+ if ( ngModel ) {
42
+
43
+ // When we add or remove elements, we need the sortable to 'refresh'
44
+ // so it can find the new/removed elements.
45
+ scope . $watch ( attrs . ngModel + '.length' , function ( ) {
46
+ // Timeout to let ng-repeat modify the DOM
47
+ $timeout ( function ( ) {
48
+ element . sortable ( 'refresh' ) ;
49
+ } ) ;
41
50
} ) ;
42
- } ) ;
43
- callbacks . start = function ( e , ui ) {
44
- // Save the starting position of dragged item
45
- ui . item . sortable = {
46
- index : ui . item . index ( ) ,
47
- cancel : function ( ) {
48
- ui . item . sortable . _isCanceled = true ;
49
- } ,
50
- isCanceled : function ( ) {
51
- return ui . item . sortable . _isCanceled ;
52
- } ,
53
- _isCanceled : false
51
+
52
+ callbacks . start = function ( e , ui ) {
53
+ // Save the starting position of dragged item
54
+ ui . item . sortable = {
55
+ index : ui . item . index ( ) ,
56
+ cancel : function ( ) {
57
+ ui . item . sortable . _isCanceled = true ;
58
+ } ,
59
+ isCanceled : function ( ) {
60
+ return ui . item . sortable . _isCanceled ;
61
+ } ,
62
+ _isCanceled : false
63
+ } ;
54
64
} ;
55
- } ;
56
- callbacks . activate = function ( ) {
57
- // We need to make a copy of the current element's contents so
58
- // we can restore it after sortable has messed it up.
59
- // This is inside activate (instead of start) in order to save
60
- // both lists when dragging between connected lists.
61
- savedNodes = element . contents ( ) ;
62
- // If this list has a placeholder (the connected lists won't),
63
- // don't inlcude it in saved nodes.
64
- var placeholder = element . sortable ( 'option' , 'placeholder' ) ;
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 && typeof placeholder . element === 'function' ) {
71
- var phElement = placeholder . element ( ) ;
72
- // workaround for jquery ui 1.9.x,
73
- // not returning jquery collection
74
- if ( ! phElement . jquery ) {
75
- phElement = angular . element ( phElement ) ;
65
+
66
+ callbacks . activate = function ( /*e, ui*/ ) {
67
+ // We need to make a copy of the current element's contents so
68
+ // we can restore it after sortable has messed it up.
69
+ // This is inside activate (instead of start) in order to save
70
+ // both lists when dragging between connected lists.
71
+ savedNodes = element . contents ( ) ;
72
+
73
+ // If this list has a placeholder (the connected lists won't),
74
+ // don't inlcude it in saved nodes.
75
+ var placeholder = element . sortable ( 'option' , 'placeholder' ) ;
76
+
77
+ // placeholder.element will be a function if the placeholder, has
78
+ // been created (placeholder will be an object). If it hasn't
79
+ // been created, either placeholder will be false if no
80
+ // placeholder class was given or placeholder.element will be
81
+ // undefined if a class was given (placeholder will be a string)
82
+ if ( placeholder && placeholder . element && typeof placeholder . element === 'function' ) {
83
+ var phElement = placeholder . element ( ) ;
84
+ // workaround for jquery ui 1.9.x,
85
+ // not returning jquery collection
86
+ if ( ! phElement . jquery ) {
87
+ phElement = angular . element ( phElement ) ;
88
+ }
89
+
90
+ // exact match with the placeholder's class attribute to handle
91
+ // the case that multiple connected sortables exist and
92
+ // the placehoilder option equals the class of sortable items
93
+ var excludes = element . find ( '[class="' + phElement . attr ( 'class' ) + '"]' ) ;
94
+
95
+ savedNodes = savedNodes . not ( excludes ) ;
76
96
}
77
- // exact match with the placeholder's class attribute to handle
78
- // the case that multiple connected sortables exist and
79
- // the placehoilder option equals the class of sortable items
80
- var excludes = element . find ( '[class="' + phElement . attr ( 'class' ) + '"]' ) ;
81
- savedNodes = savedNodes . not ( excludes ) ;
82
- }
83
- } ;
84
- callbacks . update = function ( e , ui ) {
85
- // Save current drop position but only if this is not a second
86
- // update that happens when moving between lists because then
87
- // the value will be overwritten with the old value
88
- if ( ! ui . item . sortable . received ) {
89
- ui . item . sortable . dropindex = ui . item . index ( ) ;
90
- ui . item . sortable . droptarget = ui . item . parent ( ) ;
91
- // Cancel the sort (let ng-repeat do the sort for us)
92
- // Don't cancel if this is the received list because it has
93
- // already been canceled in the other list, and trying to cancel
94
- // here will mess up the DOM.
95
- element . sortable ( 'cancel' ) ;
96
- }
97
- // Put the nodes back exactly the way they started (this is very
98
- // important because ng-repeat uses comment elements to delineate
99
- // the start and stop of repeat sections and sortable doesn't
100
- // respect their order (even if we cancel, the order of the
101
- // comments are still messed up).
102
- if ( element . sortable ( 'option' , 'helper' ) === 'clone' ) {
103
- // restore all the savedNodes except .ui-sortable-helper element
104
- // (which is placed last). That way it will be garbage collected.
105
- savedNodes = savedNodes . not ( savedNodes . last ( ) ) ;
106
- }
107
- savedNodes . appendTo ( element ) ;
108
- // If received is true (an item was dropped in from another list)
109
- // then we add the new item to this list otherwise wait until the
110
- // stop event where we will know if it was a sort or item was
111
- // moved here from another list
112
- if ( ui . item . sortable . received && ! ui . item . sortable . isCanceled ( ) ) {
113
- scope . $apply ( function ( ) {
114
- ngModel . $modelValue . splice ( ui . item . sortable . dropindex , 0 , ui . item . sortable . moved ) ;
115
- } ) ;
116
- }
117
- } ;
118
- callbacks . stop = function ( e , ui ) {
119
- // If the received flag hasn't be set on the item, this is a
120
- // normal sort, if dropindex is set, the item was moved, so move
121
- // the items in the list.
122
- if ( ! ui . item . sortable . received && 'dropindex' in ui . item . sortable && ! ui . item . sortable . isCanceled ( ) ) {
123
- scope . $apply ( function ( ) {
124
- ngModel . $modelValue . splice ( ui . item . sortable . dropindex , 0 , ngModel . $modelValue . splice ( ui . item . sortable . index , 1 ) [ 0 ] ) ;
125
- } ) ;
126
- } else {
127
- // if the item was not moved, then restore the elements
128
- // so that the ngRepeat's comment are correct.
129
- if ( ( ! ( 'dropindex' in ui . item . sortable ) || ui . item . sortable . isCanceled ( ) ) && element . sortable ( 'option' , 'helper' ) !== 'clone' ) {
130
- savedNodes . appendTo ( element ) ;
97
+ } ;
98
+
99
+ callbacks . update = function ( e , ui ) {
100
+ // Save current drop position but only if this is not a second
101
+ // update that happens when moving between lists because then
102
+ // the value will be overwritten with the old value
103
+ if ( ! ui . item . sortable . received ) {
104
+ ui . item . sortable . dropindex = ui . item . index ( ) ;
105
+ ui . item . sortable . droptarget = ui . item . parent ( ) ;
106
+
107
+ // Cancel the sort (let ng-repeat do the sort for us)
108
+ // Don't cancel if this is the received list because it has
109
+ // already been canceled in the other list, and trying to cancel
110
+ // here will mess up the DOM.
111
+ element . sortable ( 'cancel' ) ;
131
112
}
132
- }
133
- } ;
134
- callbacks . receive = function ( e , ui ) {
135
- // An item was dropped here from another list, set a flag on the
136
- // item.
137
- ui . item . sortable . received = true ;
138
- } ;
139
- callbacks . remove = function ( e , ui ) {
140
- // Remove the item from this list's model and copy data into item,
141
- // so the next list can retrive it
142
- if ( ! ui . item . sortable . isCanceled ( ) ) {
143
- scope . $apply ( function ( ) {
144
- ui . item . sortable . moved = ngModel . $modelValue . splice ( ui . item . sortable . index , 1 ) [ 0 ] ;
145
- } ) ;
146
- }
147
- } ;
148
- scope . $watch ( attrs . uiSortable , function ( newVal ) {
149
- angular . forEach ( newVal , function ( value , key ) {
150
- if ( callbacks [ key ] ) {
151
- if ( key === 'stop' ) {
152
- // call apply after stop
153
- value = combineCallbacks ( value , function ( ) {
154
- scope . $apply ( ) ;
155
- } ) ;
113
+
114
+ // Put the nodes back exactly the way they started (this is very
115
+ // important because ng-repeat uses comment elements to delineate
116
+ // the start and stop of repeat sections and sortable doesn't
117
+ // respect their order (even if we cancel, the order of the
118
+ // comments are still messed up).
119
+ if ( element . sortable ( 'option' , 'helper' ) === 'clone' ) {
120
+ // restore all the savedNodes except .ui-sortable-helper element
121
+ // (which is placed last). That way it will be garbage collected.
122
+ savedNodes = savedNodes . not ( savedNodes . last ( ) ) ;
123
+ }
124
+ savedNodes . appendTo ( element ) ;
125
+
126
+ // If received is true (an item was dropped in from another list)
127
+ // then we add the new item to this list otherwise wait until the
128
+ // stop event where we will know if it was a sort or item was
129
+ // moved here from another list
130
+ if ( ui . item . sortable . received && ! ui . item . sortable . isCanceled ( ) ) {
131
+ scope . $apply ( function ( ) {
132
+ ngModel . $modelValue . splice ( ui . item . sortable . dropindex , 0 ,
133
+ ui . item . sortable . moved ) ;
134
+ } ) ;
135
+ }
136
+ } ;
137
+
138
+ callbacks . stop = function ( e , ui ) {
139
+ // If the received flag hasn't be set on the item, this is a
140
+ // normal sort, if dropindex is set, the item was moved, so move
141
+ // the items in the list.
142
+ if ( ! ui . item . sortable . received &&
143
+ ( 'dropindex' in ui . item . sortable ) &&
144
+ ! ui . item . sortable . isCanceled ( ) ) {
145
+
146
+ scope . $apply ( function ( ) {
147
+ ngModel . $modelValue . splice (
148
+ ui . item . sortable . dropindex , 0 ,
149
+ ngModel . $modelValue . splice ( ui . item . sortable . index , 1 ) [ 0 ] ) ;
150
+ } ) ;
151
+ } else {
152
+ // if the item was not moved, then restore the elements
153
+ // so that the ngRepeat's comment are correct.
154
+ if ( ( ! ( 'dropindex' in ui . item . sortable ) || ui . item . sortable . isCanceled ( ) ) && element . sortable ( 'option' , 'helper' ) !== 'clone' ) {
155
+ savedNodes . appendTo ( element ) ;
156
156
}
157
- // wrap the callback
158
- value = combineCallbacks ( callbacks [ key ] , value ) ;
159
157
}
160
- element . sortable ( 'option' , key , value ) ;
158
+ } ;
159
+
160
+ callbacks . receive = function ( e , ui ) {
161
+ // An item was dropped here from another list, set a flag on the
162
+ // item.
163
+ ui . item . sortable . received = true ;
164
+ } ;
165
+
166
+ callbacks . remove = function ( e , ui ) {
167
+ // Remove the item from this list's model and copy data into item,
168
+ // so the next list can retrive it
169
+ if ( ! ui . item . sortable . isCanceled ( ) ) {
170
+ scope . $apply ( function ( ) {
171
+ ui . item . sortable . moved = ngModel . $modelValue . splice (
172
+ ui . item . sortable . index , 1 ) [ 0 ] ;
173
+ } ) ;
174
+ }
175
+ } ;
176
+
177
+ scope . $watch ( attrs . uiSortable , function ( newVal /*, oldVal*/ ) {
178
+ angular . forEach ( newVal , function ( value , key ) {
179
+ if ( callbacks [ key ] ) {
180
+ if ( key === 'stop' ) {
181
+ // call apply after stop
182
+ value = combineCallbacks (
183
+ value , function ( ) { scope . $apply ( ) ; } ) ;
184
+ }
185
+ // wrap the callback
186
+ value = combineCallbacks ( callbacks [ key ] , value ) ;
187
+ }
188
+ element . sortable ( 'option' , key , value ) ;
189
+ } ) ;
190
+ } , true ) ;
191
+
192
+ angular . forEach ( callbacks , function ( value , key ) {
193
+ opts [ key ] = combineCallbacks ( value , opts [ key ] ) ;
161
194
} ) ;
162
- } , true ) ;
163
- angular . forEach ( callbacks , function ( value , key ) {
164
- opts [ key ] = combineCallbacks ( value , opts [ key ] ) ;
165
- } ) ;
166
- } else {
167
- $log . info ( 'ui.sortable: ngModel not provided!' , element ) ;
195
+
196
+ } else {
197
+ $log . info ( 'ui.sortable: ngModel not provided!' , element ) ;
198
+ }
199
+
200
+ // Create sortable
201
+ element . sortable ( opts ) ;
168
202
}
169
- // Create sortable
170
- element . sortable ( opts ) ;
171
- }
172
- } ;
173
- }
174
- ] ) ;
203
+ } ;
204
+ }
205
+ ] ) ;
206
+
207
+ } ) ( window , window . angular ) ;
0 commit comments