Browse Source

Merge branch 'dev' into gh-pages

gh-pages
RubaXa 10 years ago
parent
commit
d45032a4bd
  1. 1
      .gitignore
  2. 31
      Gruntfile.js
  3. 32
      README.md
  4. 7
      Sortable.js
  5. 2
      Sortable.min.js
  6. 57
      jquery.binding.js
  7. 215
      ng-sortable.js

1
.gitignore vendored

@ -2,3 +2,4 @@ node_modules
mock.png
.*.sw*
.build*
jquery.fn.*

31
Gruntfile.js

@ -24,6 +24,11 @@ module.exports = function (grunt) {
files: {
'<%= pkg.exportName %>.min.js': ['<%= pkg.exportName %>.js']
}
},
jquery: {
files: {
'jquery.fn.sortable.min.js': 'jquery.fn.sortable.js'
}
}
},
@ -34,8 +39,30 @@ module.exports = function (grunt) {
'meteor-publish': {
command: 'meteor/publish.sh'
}
}
},
jquery: {}
});
grunt.registerTask('jquery', function (arg) {
var fs = require('fs'),
filename = 'jquery.fn.sortable.js';
grunt.log.oklns(filename);
fs.writeFileSync(
filename,
(fs.readFileSync('jquery.binding.js') + '')
.replace('/* CODE */',
(fs.readFileSync('Sortable.js') + '')
.replace(/^[\s\S]*?function[\s\S]*?(var[\s\S]+)\/\/\s+Export[\s\S]+/, '$1')
)
);
if (arg === 'min') {
grunt.task.run('uglify:jquery');
}
});
@ -50,5 +77,5 @@ module.exports = function (grunt) {
grunt.registerTask('meteor', ['meteor-test', 'meteor-publish']);
grunt.registerTask('tests', ['jshint']);
grunt.registerTask('default', ['tests', 'version', 'uglify']);
grunt.registerTask('default', ['tests', 'version', 'uglify:dist']);
};

32
README.md

@ -15,7 +15,7 @@ Demo: http://rubaxa.github.io/Sortable/
* Supports [Meteor](meteor/README.md) and [AngularJS](#ng)
* Supports any CSS library, e.g. [Bootstrap](#bs)
* Simple API
* No jQuery
* No jQuery (but there is [support](#jq))
### Usage
@ -428,9 +428,37 @@ Link to the active instance.
---
<a name="jq"></a>
### jQuery compatibility
To assemble plugin for jQuery, perform the following steps:
```bash
cd Sortable
npm install
grunt jquery
```
Now you can use `jquery.fn.sortable.js`:<br/>
(or `jquery.fn.sortable.min.js` if you run `grunt jquery:min`)
```js
$("#list").sortable({ /* options */ }); // init
$("#list").sortable("widget"); // get Sortable instance
$("#list").sortable("destroy"); // destroy Sortable instance
$("#list").sortable("{method-name}"); // call an instance method
$("#list").sortable("{method-name}", "foo", "bar"); // call an instance method with parameters
```
---
## MIT LICENSE
Copyright 2013-2014 Lebedev Konstantin <ibnRubaXa@gmail.com>
Copyright 2013-2015 Lebedev Konstantin <ibnRubaXa@gmail.com>
http://rubaxa.github.io/Sortable/
Permission is hereby granted, free of charge, to any person obtaining

7
Sortable.js

@ -57,6 +57,7 @@
evt.item = targetEl || rootEl;
evt.from = fromEl || rootEl;
evt.clone = cloneEl;
evt.oldIndex = startIndex;
evt.newIndex = newIndex;
@ -372,8 +373,10 @@
this._loopId = setInterval(this._emulateDragOver, 150);
}
else {
dataTransfer.effectAllowed = 'move';
options.setData && options.setData.call(this, dataTransfer, dragEl);
if (dataTransfer) {
dataTransfer.effectAllowed = 'move';
options.setData && options.setData.call(this, dataTransfer, dragEl);
}
_on(document, 'drop', this);
}

2
Sortable.min.js vendored

File diff suppressed because one or more lines are too long

57
jquery.binding.js

@ -0,0 +1,57 @@
/**
* jQuery plugin for Sortable
* @author RubaXa <trash@rubaxa.org>
* @license MIT
*/
(function (factory) {
"use strict";
if (typeof define === "function" && define.amd) {
define(["jquery"], factory);
}
else {
/* jshint sub:true */
factory(jQuery);
}
})(function ($) {
"use strict";
/* CODE */
/**
* jQuery plugin for Sortable
* @param {Object|String} options
* @param {..*} [args]
* @returns {jQuery|*}
*/
$.fn.sortable = function (options) {
var retVal;
this.each(function () {
var $el = $(this),
sortable = $el.data('sortable');
if (!sortable && (options instanceof Object || !options)) {
sortable = new Sortable(this, options);
$el.data('sortable', sortable);
}
if (sortable) {
if (options === 'widget') {
return sortable;
}
else if (options === 'destroy') {
sortable.destroy();
$el.removeData('sortable');
}
else if (options in sortable) {
retVal = sortable[sortable].apply(sortable, [].slice.call(arguments, 1));
}
}
});
return (retVal === void 0) ? this : retVal;
};
});

215
ng-sortable.js

@ -2,119 +2,136 @@
* @author RubaXa <trash@rubaxa.org>
* @licence MIT
*/
angular.module('ng-sortable', [])
.constant('$version', '0.3.2')
.directive('ngSortable', ['$parse', function ($parse) {
'use strict';
var removed;
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];
ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*([^\s]+)\s+in\s+([^\s|]+)/);
var itemExpr = $parse(ngRepeat[1]);
var itemsExpr = $parse(ngRepeat[2]);
(function (factory) {
'use strict';
if (window.angular && window.Sortable) {
factory(angular, Sortable);
}
else if (typeof define === 'function' && define.amd) {
define(['angular', 'sortable'], factory);
}
})(function (angular, Sortable) {
'use strict';
angular.module('ng-sortable', [])
.constant('$version', '0.3.2')
.directive('ngSortable', ['$parse', function ($parse) {
var removed;
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];
ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*([^\s]+)\s+in\s+([^\s|]+)/);
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);
}
};
}
return {
item: function (el) {
return itemExpr(angular.element(el).scope());
},
items: function () {
return itemsExpr(scope);
}
};
}
// Export
return {
restrict: 'AC',
link: function (scope, $el, attrs) {
var el = $el[0],
ngSortable = attrs.ngSortable,
options = scope.$eval(ngSortable) || {},
source = getSource(el),
sortable
;
'Start End Add Update Remove Sort'.split(' ').forEach(function (name) {
options['on' + name] = options['on' + name] || function () {};
});
// Export
return {
restrict: 'AC',
link: function (scope, $el, attrs) {
var el = $el[0],
ngSortable = attrs.ngSortable,
options = scope.$eval(ngSortable) || {},
source = getSource(el),
sortable
;
function _sync(evt) {
var oldIndex = evt.oldIndex,
newIndex = evt.newIndex,
items = source.items();
'Start End Add Update Remove Sort'.split(' ').forEach(function (name) {
options['on' + name] = options['on' + name] || function () {};
});
if (el !== evt.from) {
var prevSource = getSource(evt.from),
prevItems = prevSource.items();
oldIndex = prevItems.indexOf(prevSource.item(evt.item));
removed = prevItems.splice(oldIndex, 1)[0];
function _sync(evt) {
var oldIndex = evt.oldIndex,
newIndex = evt.newIndex,
items = source.items();
items.splice(newIndex, 0, removed);
if (el !== evt.from) {
var prevSource = getSource(evt.from),
prevItems = prevSource.items();
if (evt.clone) {
newIndex = Sortable.utils.index(evt.clone);
prevItems.splice(newIndex, 0, removed);
oldIndex = prevItems.indexOf(prevSource.item(evt.item));
removed = prevItems.splice(oldIndex, 1)[0];
evt.from.removeChild(evt.clone);
}
items.splice(newIndex, 0, removed);
evt.from.appendChild(evt.item); // revert element
} else {
items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]);
}
evt.from.appendChild(evt.item); // revert element
} else {
items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]);
scope.$apply();
}
scope.$apply();
}
sortable = Sortable.create(el, Object.keys(options).reduce(function (opts, name) {
opts[name] = opts[name] || options[name];
return opts;
}, {
onStart: function () {
options.onStart(source.items());
},
onEnd: function () {
options.onEnd(source.items());
},
onAdd: function (evt) {
_sync(evt);
options.onAdd(source.items(), removed);
},
onUpdate: function (evt) {
_sync(evt);
options.onUpdate(source.items(), source.item(evt.item));
},
onRemove: function () {
options.onRemove(source.items(), removed);
},
onSort: function () {
options.onSort(source.items());
}
}));
$el.on('$destroy', function () {
sortable.destroy();
sortable = null;
});
sortable = Sortable.create(el, Object.keys(options).reduce(function (opts, name) {
opts[name] = opts[name] || options[name];
return opts;
}, {
onStart: function () {
options.onStart(source.items());
},
onEnd: function () {
options.onEnd(source.items());
},
onAdd: function (evt) {
_sync(evt);
options.onAdd(source.items(), removed);
},
onUpdate: function (evt) {
_sync(evt);
options.onUpdate(source.items(), source.item(evt.item));
},
onRemove: function () {
options.onRemove(source.items(), removed);
},
onSort: function () {
options.onSort(source.items());
}
}));
$el.on('$destroy', function () {
sortable.destroy();
sortable = null;
});
if (!/{|}/.test(ngSortable)) { // todo: ugly
angular.forEach(['sort', 'disabled', 'draggable', 'handle', 'animation'], function (name) {
scope.$watch(ngSortable + '.' + name, function (value) {
if (value !== void 0) {
options[name] = value;
sortable.option(name, value);
}
if (ngSortable && !/{|}/.test(ngSortable)) { // todo: ugly
angular.forEach(['sort', 'disabled', 'draggable', 'handle', 'animation'], function (name) {
scope.$watch(ngSortable + '.' + name, function (value) {
if (value !== void 0) {
options[name] = value;
sortable.option(name, value);
}
});
});
});
}
}
}
};
}])
;
};
}]);
});

Loading…
Cancel
Save