js/src/Sortable.js
/* ----
* Html5 Sortable
* http://github.com/clippings/html5-sortable
* ----
* Copyright 2015 Clippings Ltd.
* Licensed under BSD (https://github.com/clippings/html5-sortable/blob/master/LICENSE)
* ----
*/
/* exported Sortable */
var Sortable = (function ($) {
'use strict'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'html5Sortable'
var DATA_KEY = 'html5-sortable'
var EVENT_KEY = '.' + DATA_KEY
var Event = {
START : 'dragstart' + EVENT_KEY + ' touchstart ' + EVENT_KEY,
OVER : 'dragover' + EVENT_KEY + ' touchmove ' + EVENT_KEY,
END : 'dragend' + EVENT_KEY + ' touchcancel ' + EVENT_KEY,
DROP : 'drop' + EVENT_KEY + ' touchend ' + EVENT_KEY,
LEAVE : 'dragleave' + EVENT_KEY,
SORT : 'sort'
}
var Selector = {
CONTAINER : '[data-arrange="html5-sortable"]',
ITEM : '[data-arrange="html5-sortable"] > [draggable]'
}
var Default = {
cursor: 'sortable-cursor',
field: false
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
/**
* @param {jQuery} element
* @param {Object} options
*/
function Sortable(element, options) {
this.$element = $(element)
this._options = this._getOptions(options || {})
this.$cursor = $([])
}
// getters
Sortable.NAME = NAME
Sortable.DATA_KEY = DATA_KEY
Sortable.EVENT_KEY = EVENT_KEY
Sortable.Default = Default
Sortable.prototype.options = function () {
return this._options
}
// public
/**
* Update an option directly
*
* @param {String} name
* @param {mixed} value
*/
Sortable.prototype.option = function (name, value) {
this._options[name] = value
}
/**
* Clear cursor
*/
Sortable.prototype.end = function () {
this.$cursor.removeClass(this._options.cursor)
}
/**
* Set an item to be the current "cursor"
*
* @param {jQuery} $cursor
*/
Sortable.prototype.cursor = function ($cursor) {
this.end()
this.$cursor = $cursor.addClass(this._options.cursor)
}
/**
* Swap two elements, trigger sort event and update field values
*
* @param {jQuery} $widget
* @param {jQuery} $cursor
*/
Sortable.prototype.reposition = function ($widget, $cursor) {
if ($cursor.parent().is($widget.parent()) && $cursor.index() > $widget.index()) {
$widget.insertAfter($cursor)
} else {
$widget.insertBefore($cursor)
}
$widget.trigger(Event.SORT, [$cursor])
this.update()
}
/**
* Update all sortable field values
*/
Sortable.prototype.update = function () {
var field = this._options.field
if (field) {
this.$element.children('[draggable]').each(function () {
$(this).find(field).val($(this).index())
})
}
}
// private
// ------------------------------------------------------------------------
Sortable.prototype._getOptions = function (options) {
return $.extend(true, {}, Default, options)
}
// static
Sortable._jQueryInterface = function (config, a1, a2, a3) {
return this.each(function () {
var $this = $(this)
var data = $this.data(DATA_KEY)
var _config = $.extend(
true,
{},
Default,
$this.data(),
typeof config === 'object' && config
)
if (!data) {
data = new Sortable(this, _config)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
data[config](a1, a2, a3)
}
})
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(Event.START, Selector.ITEM, function (event) {
Store.set(event.originalEvent, this)
})
.on(Event.OVER, Selector.ITEM, function (event) {
var $widget = $(Store.get(event.originalEvent))
var $this = $(this)
var $sortable = $this.parent()
if ($widget.length) {
event.preventDefault()
$sortable[NAME]('cursor', $this)
}
})
.on(Event.END, Selector.CONTAINER, function () {
$(this)[NAME]('end')
})
.on(Event.LEAVE, Selector.CONTAINER, function (event) {
if ($(event.target).is(Selector.ITEM)) {
event.preventDefault()
$(this)[NAME]('end')
}
})
.on(Event.DROP, Selector.ITEM, function (event) {
var $widget = $(Store.get(event.originalEvent))
if ($widget.length) {
var $this = $(this)
var $sortable = $this.parent()
event.preventDefault()
$sortable[NAME]('reposition', $widget, $this)
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Sortable._jQueryInterface
$.fn[NAME].Sortable = Sortable
return Sortable
})(jQuery)