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

	if (window.angular && window.Sortable) {
		factory(angular, Sortable);
	}
	else if (typeof define === 'function' && define.amd) {
R
RubaXa 已提交
12
		define(['angular', './Sortable'], factory);
R
RubaXa 已提交
13 14 15 16
	}
})(function (angular, Sortable) {
	'use strict';

R
RubaXa 已提交
17 18 19 20 21 22 23 24 25 26

	/**
	 * @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 已提交
27
	angular.module('ng-sortable', [])
R
RubaXa 已提交
28
		.constant('version', '0.3.7')
R
RubaXa 已提交
29
		.directive('ngSortable', ['$parse', function ($parse) {
R
RubaXa 已提交
30 31
			var removed,
				nextSibling;
R
RubaXa 已提交
32 33 34 35 36 37 38 39 40

			function getSource(el) {
				var scope = angular.element(el).scope();
				var ngRepeat = [].filter.call(el.childNodes, function (node) {
					return (
							(node.nodeType === 8) &&
							(node.nodeValue.indexOf('ngRepeat:') !== -1)
						);
				})[0];
R
RubaXa 已提交
41

R
RubaXa 已提交
42 43 44 45 46
				if (!ngRepeat) {
					// Without ng-repeat
					return null;
				}

R
RubaXa 已提交
47 48
				// tests: http://jsbin.com/kosubutilo/1/edit?js,output
				ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*(?:\(.*?,\s*)?([^\s)]+)[\s)]+in\s+([^\s|]+)/);
R
RubaXa 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61

				var itemExpr = $parse(ngRepeat[1]);
				var itemsExpr = $parse(ngRepeat[2]);

				return {
					item: function (el) {
						return itemExpr(angular.element(el).scope());
					},
					items: function () {
						return itemsExpr(scope);
					}
				};
			}
62

R
RubaXa 已提交
63

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


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

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


R
RubaXa 已提交
90
					function _sync(/**Event*/evt) {
R
RubaXa 已提交
91 92 93 94 95
						if (!source) {
							// Without ng-repeat
							return;
						}

R
RubaXa 已提交
96 97 98
						var oldIndex = evt.oldIndex,
							newIndex = evt.newIndex,
							items = source.items();
R
RubaXa 已提交
99

R
RubaXa 已提交
100 101 102
						if (el !== evt.from) {
							var prevSource = getSource(evt.from),
								prevItems = prevSource.items();
R
RubaXa 已提交
103

R
RubaXa 已提交
104
							oldIndex = prevItems.indexOf(prevSource.item(evt.item));
R
RubaXa 已提交
105
							removed = prevItems[oldIndex];
R
RubaXa 已提交
106

R
RubaXa 已提交
107 108
							if (evt.clone) {
								evt.from.removeChild(evt.clone);
109
								removed = angular.copy(removed);
R
RubaXa 已提交
110
							}
R
RubaXa 已提交
111 112 113
							else {
								prevItems.splice(oldIndex, 1);
							}
R
RubaXa 已提交
114

R
RubaXa 已提交
115 116 117 118 119
							items.splice(newIndex, 0, removed);

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

R
RubaXa 已提交
123
						scope.$apply();
R
RubaXa 已提交
124
					}
125 126


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

					$el.on('$destroy', function () {
C
c4605 已提交
158 159 160
						angular.forEach(watchers, function (/** Function */unwatch) {
							unwatch();
						});
R
RubaXa 已提交
161
						sortable.destroy();
C
c4605 已提交
162
						watchers = null;
R
RubaXa 已提交
163
						sortable = null;
R
RubaXa 已提交
164
						nextSibling = null;
R
RubaXa 已提交
165
					});
166

167 168 169 170 171 172 173 174 175 176
					angular.forEach([
						'sort', 'disabled', 'draggable', 'handle', 'animation',
						'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 已提交
177
								}
178 179 180
							}
						}));
					});
R
RubaXa 已提交
181
				}
R
RubaXa 已提交
182
			};
R
RubaXa 已提交
183
		}]);
R
RubaXa 已提交
184
});