ng-sortable.js 4.8 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),
R
RubaXa 已提交
74 75 76
						sortable
					;

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

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

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


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

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

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

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

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

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

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

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

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


R
RubaXa 已提交
130 131 132 133
					sortable = Sortable.create(el, Object.keys(options).reduce(function (opts, name) {
						opts[name] = opts[name] || options[name];
						return opts;
					}, {
R
* evt  
RubaXa 已提交
134
						onStart: function (/**Event*/evt) {
R
RubaXa 已提交
135
							nextSibling = evt.item.nextSibling;
R
RubaXa 已提交
136
							_emitEvent(evt);
R
RubaXa 已提交
137
							scope.$apply();
R
RubaXa 已提交
138
						},
R
RubaXa 已提交
139 140
						onEnd: function (/**Event*/evt) {
							_emitEvent(evt, removed);
R
RubaXa 已提交
141
							scope.$apply();
R
RubaXa 已提交
142
						},
R
RubaXa 已提交
143
						onAdd: function (/**Event*/evt) {
R
RubaXa 已提交
144
							_sync(evt);
R
RubaXa 已提交
145
							_emitEvent(evt, removed);
R
RubaXa 已提交
146
							scope.$apply();
R
RubaXa 已提交
147
						},
R
RubaXa 已提交
148
						onUpdate: function (/**Event*/evt) {
R
RubaXa 已提交
149
							_sync(evt);
R
RubaXa 已提交
150
							_emitEvent(evt);
R
RubaXa 已提交
151
						},
R
RubaXa 已提交
152 153
						onRemove: function (/**Event*/evt) {
							_emitEvent(evt, removed);
R
RubaXa 已提交
154
						},
R
RubaXa 已提交
155
						onSort: function (/**Event*/evt) {
R
RubaXa 已提交
156
							_emitEvent(evt);
R
RubaXa 已提交
157 158 159 160
						}
					}));

					$el.on('$destroy', function () {
C
c4605 已提交
161 162 163
						angular.forEach(watchers, function (/** Function */unwatch) {
							unwatch();
						});
R
RubaXa 已提交
164

R
RubaXa 已提交
165
						sortable.destroy();
R
RubaXa 已提交
166 167 168

						el[expando] = null;
						el = null;
C
c4605 已提交
169
						watchers = null;
R
RubaXa 已提交
170
						sortable = null;
R
RubaXa 已提交
171
						nextSibling = null;
R
RubaXa 已提交
172
					});
173

174
					angular.forEach([
175
						'sort', 'disabled', 'draggable', 'handle', 'animation', 'group', 'ghostClass', 'filter',
176 177 178 179 180 181 182 183
						'onStart', 'onEnd', 'onAdd', 'onUpdate', 'onRemove', 'onSort'
					], 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 已提交
184
								}
185 186 187
							}
						}));
					});
R
RubaXa 已提交
188
				}
R
RubaXa 已提交
189
			};
R
RubaXa 已提交
190
		}]);
R
RubaXa 已提交
191
});