ng-sortable.js 4.3 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 67 68 69 70 71 72 73 74 75
			// Export
			return {
				restrict: 'AC',
				link: function (scope, $el, attrs) {
					var el = $el[0],
						ngSortable = attrs.ngSortable,
						options = scope.$eval(ngSortable) || {},
						source = getSource(el),
						sortable
					;


R
RubaXa 已提交
76 77 78 79 80
					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 已提交
81
							model: item || source && source.item(evt.item),
R
RubaXa 已提交
82
							models: source && source.items(),
R
RubaXa 已提交
83 84 85 86
							oldIndex: evt.oldIndex,
							newIndex: evt.newIndex
						});
					}
87 88


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

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

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

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

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

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

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

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


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

					$el.on('$destroy', function () {
						sortable.destroy();
						sortable = null;
R
RubaXa 已提交
159
						nextSibling = null;
R
RubaXa 已提交
160
					});
161

R
RubaXa 已提交
162
					if (ngSortable && !/{|}/.test(ngSortable)) { // todo: ugly
R
RubaXa 已提交
163 164 165 166
						angular.forEach([
							'sort', 'disabled', 'draggable', 'handle', 'animation',
							'onStart', 'onEnd', 'onAdd', 'onUpdate', 'onRemove', 'onSort'
						], function (name) {
R
RubaXa 已提交
167 168 169
							scope.$watch(ngSortable + '.' + name, function (value) {
								if (value !== void 0) {
									options[name] = value;
170 171 172 173

									if (!/^on[A-Z]/.test(name)) {
										sortable.option(name, value);
									}
R
RubaXa 已提交
174 175
								}
							});
R
RubaXa 已提交
176
						});
R
RubaXa 已提交
177
					}
R
RubaXa 已提交
178
				}
R
RubaXa 已提交
179
			};
R
RubaXa 已提交
180
		}]);
R
RubaXa 已提交
181
});