ng-sortable.js 5.0 KB
Newer Older
1 2 3 4
/**
 * @author RubaXa <trash@rubaxa.org>
 * @licence MIT
 */
R
RubaXa 已提交
5 6 7
(function (factory) {
	'use strict';

8
	if (typeof define === 'function' && define.amd) {
R
RubaXa 已提交
9
		define(['angular', './Sortable'], factory);
R
RubaXa 已提交
10
	}
11 12 13 14 15 16 17 18
	else if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
		require('angular');
		factory(angular, require('./Sortable'));
		module.exports = 'ng-sortable';
	}
	else if (window.angular && window.Sortable) {
		factory(angular, Sortable);
	}
R
RubaXa 已提交
19 20 21
})(function (angular, Sortable) {
	'use strict';

R
RubaXa 已提交
22 23 24 25 26 27 28 29 30

	/**
	 * @typedef   {Object}        ngSortEvent
	 * @property  {*}             model      List item
	 * @property  {Object|Array}  models     List of items
	 * @property  {number}        oldIndex   before sort
	 * @property  {number}        newIndex   after sort
	 */

R
RubaXa 已提交
31
	var expando = 'Sortable:ng-sortable';
R
RubaXa 已提交
32

R
RubaXa 已提交
33
	angular.module('ng-sortable', [])
R
RubaXa 已提交
34
		.constant('ngSortableVersion', '0.4.0')
35 36
		.constant('ngSortableConfig', {})
		.directive('ngSortable', ['$parse', 'ngSortableConfig', function ($parse, ngSortableConfig) {
R
RubaXa 已提交
37
			var removed,
R
RubaXa 已提交
38 39 40 41 42 43 44 45 46 47 48
				nextSibling,
				getSourceFactory = function getSourceFactory(el, scope) {
					var ngRepeat = [].filter.call(el.childNodes, function (node) {
						return (
								(node.nodeType === 8) &&
								(node.nodeValue.indexOf('ngRepeat:') !== -1)
							);
					})[0];

					if (!ngRepeat) {
						// Without ng-repeat
R
RubaXa 已提交
49 50 51
						return function () {
							return null;
						};
R
RubaXa 已提交
52
					}
R
RubaXa 已提交
53

R
RubaXa 已提交
54 55
					// tests: http://jsbin.com/kosubutilo/1/edit?js,output
					ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*(?:\(.*?,\s*)?([^\s)]+)[\s)]+in\s+([^\s|]+)/);
R
RubaXa 已提交
56

R
RubaXa 已提交
57
					var itemsExpr = $parse(ngRepeat[2]);
R
RubaXa 已提交
58

R
RubaXa 已提交
59 60 61
					return function () {
						return itemsExpr(scope.$parent) || [];
					};
R
RubaXa 已提交
62
				};
63

R
RubaXa 已提交
64

R
RubaXa 已提交
65 66 67
			// Export
			return {
				restrict: 'AC',
68
				scope: { ngSortable: "=?" },
R
RubaXa 已提交
69
				link: function (scope, $el) {
R
RubaXa 已提交
70
					var el = $el[0],
71
						options = angular.extend(scope.ngSortable || {}, ngSortableConfig),
C
c4605 已提交
72
						watchers = [],
R
RubaXa 已提交
73
						getSource = getSourceFactory(el, scope),
L
livelazily 已提交
74
						offDestroy,
R
RubaXa 已提交
75 76 77
						sortable
					;

R
RubaXa 已提交
78
					el[expando] = getSource;
R
RubaXa 已提交
79

R
RubaXa 已提交
80 81
					function _emitEvent(/**Event*/evt, /*Mixed*/item) {
						var name = 'on' + evt.type.charAt(0).toUpperCase() + evt.type.substr(1);
R
RubaXa 已提交
82
						var source = getSource();
R
RubaXa 已提交
83 84 85

						/* jshint expr:true */
						options[name] && options[name]({
R
RubaXa 已提交
86 87
							model: item || source[evt.newIndex],
							models: source,
R
RubaXa 已提交
88 89 90 91
							oldIndex: evt.oldIndex,
							newIndex: evt.newIndex
						});
					}
92 93


R
RubaXa 已提交
94
					function _sync(/**Event*/evt) {
R
RubaXa 已提交
95 96 97
						var items = getSource();

						if (!items) {
R
RubaXa 已提交
98 99 100 101
							// Without ng-repeat
							return;
						}

R
RubaXa 已提交
102
						var oldIndex = evt.oldIndex,
R
RubaXa 已提交
103
							newIndex = evt.newIndex;
R
RubaXa 已提交
104

R
RubaXa 已提交
105
						if (el !== evt.from) {
R
RubaXa 已提交
106
							var prevItems = evt.from[expando]();
R
RubaXa 已提交
107

R
RubaXa 已提交
108
							removed = prevItems[oldIndex];
R
RubaXa 已提交
109

R
RubaXa 已提交
110
							if (evt.clone) {
111
								removed = angular.copy(removed);
R
RubaXa 已提交
112 113
								prevItems.splice(Sortable.utils.index(evt.clone), 0, prevItems.splice(oldIndex, 1)[0]);
								evt.from.removeChild(evt.clone);
R
RubaXa 已提交
114
							}
R
RubaXa 已提交
115 116 117
							else {
								prevItems.splice(oldIndex, 1);
							}
R
RubaXa 已提交
118

R
RubaXa 已提交
119 120 121 122 123
							items.splice(newIndex, 0, removed);

							evt.from.insertBefore(evt.item, nextSibling); // revert element
						}
						else {
R
RubaXa 已提交
124 125
							items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]);
						}
R
RubaXa 已提交
126

R
RubaXa 已提交
127
						scope.$apply();
R
RubaXa 已提交
128
					}
129

R
RubaXa 已提交
130
					function _destroy() {
L
livelazily 已提交
131
						offDestroy();
132

R
RubaXa 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
						angular.forEach(watchers, function (/** Function */unwatch) {
							unwatch();
						});

						sortable.destroy();

						el[expando] = null;
						el = null;
						watchers = null;
						sortable = null;
						nextSibling = null;
					}


					// Initialization
R
RubaXa 已提交
148 149 150 151
					sortable = Sortable.create(el, Object.keys(options).reduce(function (opts, name) {
						opts[name] = opts[name] || options[name];
						return opts;
					}, {
R
* evt  
RubaXa 已提交
152
						onStart: function (/**Event*/evt) {
153
							nextSibling = evt.from === evt.item.parentNode ? evt.item.nextSibling : evt.clone.nextSibling;
R
RubaXa 已提交
154
							_emitEvent(evt);
R
RubaXa 已提交
155
							scope.$apply();
R
RubaXa 已提交
156
						},
R
RubaXa 已提交
157 158
						onEnd: function (/**Event*/evt) {
							_emitEvent(evt, removed);
R
RubaXa 已提交
159
							scope.$apply();
R
RubaXa 已提交
160
						},
R
RubaXa 已提交
161
						onAdd: function (/**Event*/evt) {
R
RubaXa 已提交
162
							_sync(evt);
R
RubaXa 已提交
163
							_emitEvent(evt, removed);
R
RubaXa 已提交
164
							scope.$apply();
R
RubaXa 已提交
165
						},
R
RubaXa 已提交
166
						onUpdate: function (/**Event*/evt) {
R
RubaXa 已提交
167
							_sync(evt);
R
RubaXa 已提交
168
							_emitEvent(evt);
R
RubaXa 已提交
169
						},
R
RubaXa 已提交
170 171
						onRemove: function (/**Event*/evt) {
							_emitEvent(evt, removed);
R
RubaXa 已提交
172
						},
R
RubaXa 已提交
173
						onSort: function (/**Event*/evt) {
R
RubaXa 已提交
174
							_emitEvent(evt);
R
RubaXa 已提交
175 176 177
						}
					}));

R
RubaXa 已提交
178
					// Create watchers for `options`
179
					angular.forEach([
180
						'sort', 'disabled', 'draggable', 'handle', 'animation', 'group', 'ghostClass', 'filter',
R
RubaXa 已提交
181
						'onStart', 'onEnd', 'onAdd', 'onUpdate', 'onRemove', 'onSort', 'onMove', 'onClone'
182 183 184 185 186 187 188
					], function (name) {
						watchers.push(scope.$watch('ngSortable.' + name, function (value) {
							if (value !== void 0) {
								options[name] = value;

								if (!/^on[A-Z]/.test(name)) {
									sortable.option(name, value);
R
RubaXa 已提交
189
								}
190 191 192
							}
						}));
					});
R
RubaXa 已提交
193

L
livelazily 已提交
194
					offDestroy = scope.$on('$destroy', _destroy);
R
RubaXa 已提交
195
				}
R
RubaXa 已提交
196
			};
R
RubaXa 已提交
197
		}]);
R
RubaXa 已提交
198
});