123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- ;(function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- define(['jquery'], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory(require('jquery'));
- } else {
- root.sortable = factory(root.jQuery);
- }
- }(this, function($) {
- /*
- * HTML5 Sortable jQuery Plugin
- * https://github.com/voidberg/html5sortable
- *
- * Original code copyright 2012 Ali Farhadi.
- * This version is mantained by Alexandru Badiu <andu@ctrlz.ro> & Lukas Oppermann <lukas@vea.re>
- *
- *
- * Released under the MIT license.
- */
- 'use strict';
- /*
- * variables global to the plugin
- */
- var dragging;
- var draggingHeight;
- var placeholders = $();
- var sortables = [];
- /*
- * remove event handlers from items
- * @param [jquery Collection] items
- * @info event.h5s (jquery way of namespacing events, to bind multiple handlers to the event)
- */
- var _removeItemEvents = function(items) {
- items.off('dragstart.h5s');
- items.off('dragend.h5s');
- items.off('selectstart.h5s');
- items.off('dragover.h5s');
- items.off('dragenter.h5s');
- items.off('drop.h5s');
- };
- /*
- * remove event handlers from sortable
- * @param [jquery Collection] sortable
- * @info event.h5s (jquery way of namespacing events, to bind multiple handlers to the event)
- */
- var _removeSortableEvents = function(sortable) {
- sortable.off('dragover.h5s');
- sortable.off('dragenter.h5s');
- sortable.off('drop.h5s');
- };
- /*
- * attache ghost to dataTransfer object
- * @param [event] original event
- * @param [object] ghost-object with item, x and y coordinates
- */
- var _attachGhost = function(event, ghost) {
- // this needs to be set for HTML5 drag & drop to work
- event.dataTransfer.effectAllowed = 'move';
- event.dataTransfer.setData('text', '');
- // check if setDragImage method is available
- if (event.dataTransfer.setDragImage) {
- event.dataTransfer.setDragImage(ghost.item, ghost.x, ghost.y);
- }
- };
- /**
- * _addGhostPos clones the dragged item and adds it as a Ghost item
- * @param [object] event - the event fired when dragstart is triggered
- * @param [object] ghost - .item = node, draggedItem = jQuery collection
- */
- var _addGhostPos = function(e, ghost) {
- if (!ghost.x) {
- ghost.x = parseInt(e.pageX - ghost.draggedItem.offset().left);
- }
- if (!ghost.y) {
- ghost.y = parseInt(e.pageY - ghost.draggedItem.offset().top);
- }
- return ghost;
- };
- /**
- * _makeGhost decides which way to make a ghost and passes it to attachGhost
- * @param [jQuery selection] $draggedItem - the item that the user drags
- */
- var _makeGhost = function($draggedItem) {
- return {
- item: $draggedItem[0],
- draggedItem: $draggedItem
- };
- };
- /**
- * _getGhost constructs ghost and attaches it to dataTransfer
- * @param [event] event - the original drag event object
- * @param [jQuery selection] $draggedItem - the item that the user drags
- * @param [object] ghostOpt - the ghost options
- */
- // TODO: could $draggedItem be replaced by event.target in all instances
- var _getGhost = function(event, $draggedItem) {
- // add ghost item & draggedItem to ghost object
- var ghost = _makeGhost($draggedItem);
- // attach ghost position
- ghost = _addGhostPos(event, ghost);
- // attach ghost to dataTransfer
- _attachGhost(event, ghost);
- };
- /*
- * return options if not set on sortable already
- * @param [object] soptions
- * @param [object] options
- */
- var _getOptions = function(soptions, options) {
- if (typeof soptions === 'undefined') {
- return options;
- }
- return soptions;
- };
- /*
- * remove data from sortable
- * @param [jquery Collection] a single sortable
- */
- var _removeSortableData = function(sortable) {
- sortable.removeData('opts');
- sortable.removeData('connectWith');
- sortable.removeData('items');
- sortable.removeAttr('aria-dropeffect');
- };
- /*
- * remove data from items
- * @param [jquery Collection] items
- */
- var _removeItemData = function(items) {
- items.removeAttr('aria-grabbed');
- items.removeAttr('draggable');
- items.removeAttr('role');
- };
- /*
- * check if two lists are connected
- * @param [jquery Collection] items
- */
- var _listsConnected = function(curList, destList) {
- if (curList[0] === destList[0]) {
- return true;
- }
- if (curList.data('connectWith') !== undefined) {
- return curList.data('connectWith') === destList.data('connectWith');
- }
- return false;
- };
- /*
- * destroy the sortable
- * @param [jquery Collection] a single sortable
- */
- var _destroySortable = function(sortable) {
- var opts = sortable.data('opts') || {};
- var items = sortable.children(opts.items);
- var handles = opts.handle ? items.find(opts.handle) : items;
- // remove event handlers & data from sortable
- _removeSortableEvents(sortable);
- _removeSortableData(sortable);
- // remove event handlers & data from items
- handles.off('mousedown.h5s');
- _removeItemEvents(items);
- _removeItemData(items);
- };
- /*
- * enable the sortable
- * @param [jquery Collection] a single sortable
- */
- var _enableSortable = function(sortable) {
- var opts = sortable.data('opts');
- var items = sortable.children(opts.items);
- var handles = opts.handle ? items.find(opts.handle) : items;
- sortable.attr('aria-dropeffect', 'move');
- handles.attr('draggable', 'true');
- // IE FIX for ghost
- // can be disabled as it has the side effect that other events
- // (e.g. click) will be ignored
- var spanEl = (document || window.document).createElement('span');
- if (typeof spanEl.dragDrop === 'function' && !opts.disableIEFix) {
- handles.on('mousedown.h5s', function() {
- if (items.index(this) !== -1) {
- this.dragDrop();
- } else {
- $(this).parents(opts.items)[0].dragDrop();
- }
- });
- }
- };
- /*
- * disable the sortable
- * @param [jquery Collection] a single sortable
- */
- var _disableSortable = function(sortable) {
- var opts = sortable.data('opts');
- var items = sortable.children(opts.items);
- var handles = opts.handle ? items.find(opts.handle) : items;
- sortable.attr('aria-dropeffect', 'none');
- handles.attr('draggable', false);
- handles.off('mousedown.h5s');
- };
- /*
- * reload the sortable
- * @param [jquery Collection] a single sortable
- * @description events need to be removed to not be double bound
- */
- var _reloadSortable = function(sortable) {
- var opts = sortable.data('opts');
- var items = sortable.children(opts.items);
- var handles = opts.handle ? items.find(opts.handle) : items;
- // remove event handlers from items
- _removeItemEvents(items);
- handles.off('mousedown.h5s');
- // remove event handlers from sortable
- _removeSortableEvents(sortable);
- };
- /*
- * public sortable object
- * @param [object|string] options|method
- */
- var sortable = function(selector, options) {
- var $sortables = $(selector);
- var method = String(options);
- options = $.extend({
- connectWith: false,
- placeholder: null,
- // dragImage can be null or a jQuery element
- dragImage: null,
- disableIEFix: false,
- placeholderClass: 'sortable-placeholder',
- draggingClass: 'sortable-dragging',
- hoverClass: false
- }, options);
- /* TODO: maxstatements should be 25, fix and remove line below */
- /*jshint maxstatements:false */
- return $sortables.each(function() {
- var $sortable = $(this);
- if (/enable|disable|destroy/.test(method)) {
- sortable[method]($sortable);
- return;
- }
- // get options & set options on sortable
- options = _getOptions($sortable.data('opts'), options);
- $sortable.data('opts', options);
- // reset sortable
- _reloadSortable($sortable);
- // initialize
- var items = $sortable.children(options.items);
- var index;
- var startParent;
- var newParent;
- var placeholder = (options.placeholder === null) ? $('<' + (/^ul|ol$/i.test(this.tagName) ? 'li' : 'div') + ' class="' + options.placeholderClass + '"/>') : $(options.placeholder).addClass(options.placeholderClass);
- // setup sortable ids
- if (!$sortable.attr('data-sortable-id')) {
- var id = sortables.length;
- sortables[id] = $sortable;
- $sortable.attr('data-sortable-id', id);
- items.attr('data-item-sortable-id', id);
- }
- $sortable.data('items', options.items);
- placeholders = placeholders.add(placeholder);
- if (options.connectWith) {
- $sortable.data('connectWith', options.connectWith);
- }
- _enableSortable($sortable);
- items.attr('role', 'option');
- items.attr('aria-grabbed', 'false');
- // Mouse over class
- if (options.hoverClass) {
- var hoverClass = 'sortable-over';
- if (typeof options.hoverClass === 'string') {
- hoverClass = options.hoverClass;
- }
- items.hover(function() {
- $(this).addClass(hoverClass);
- }, function() {
- $(this).removeClass(hoverClass);
- });
- }
- // Handle drag events on draggable items
- items.on('dragstart.h5s', function(e) {
- e.stopImmediatePropagation();
- if (options.dragImage) {
- _attachGhost(e.originalEvent, {
- item: options.dragImage,
- x: 0,
- y: 0
- });
- console.log('WARNING: dragImage option is deprecated' +
- ' and will be removed in the future!');
- } else {
- // add transparent clone or other ghost to cursor
- _getGhost(e.originalEvent, $(this), options.dragImage);
- }
- // cache selsection & add attr for dragging
- dragging = $(this);
- dragging.addClass(options.draggingClass);
- dragging.attr('aria-grabbed', 'true');
- // grab values
- index = dragging.index();
- draggingHeight = dragging.height();
- startParent = $(this).parent();
- // trigger sortstar update
- dragging.parent().triggerHandler('sortstart', {
- item: dragging,
- placeholder: placeholder,
- startparent: startParent
- });
- });
- // Handle drag events on draggable items
- items.on('dragend.h5s', function() {
- if (!dragging) {
- return;
- }
- // remove dragging attributes and show item
- dragging.removeClass(options.draggingClass);
- dragging.attr('aria-grabbed', 'false');
- dragging.show();
- placeholders.detach();
- newParent = $(this).parent();
- dragging.parent().triggerHandler('sortstop', {
- item: dragging,
- startparent: startParent,
- });
- if (index !== dragging.index() ||
- startParent.get(0) !== newParent.get(0)) {
- dragging.parent().triggerHandler('sortupdate', {
- item: dragging,
- index: newParent.children(newParent.data('items')).index(dragging),
- oldindex: items.index(dragging),
- elementIndex: dragging.index(),
- oldElementIndex: index,
- startparent: startParent,
- endparent: newParent
- });
- }
- dragging = null;
- draggingHeight = null;
- });
- // Handle drop event on sortable & placeholder
- // TODO: REMOVE placeholder?????
- $(this).add([placeholder]).on('drop.h5s', function(e) {
- if (!_listsConnected($sortable, $(dragging).parent())) {
- return;
- }
- e.stopPropagation();
- placeholders.filter(':visible').after(dragging);
- dragging.trigger('dragend.h5s');
- return false;
- });
- // Handle dragover and dragenter events on draggable items
- items.add([this]).on('dragover.h5s dragenter.h5s', function(e) {
- if (!_listsConnected($sortable, $(dragging).parent())) {
- return;
- }
- e.preventDefault();
- e.originalEvent.dataTransfer.dropEffect = 'move';
- if (items.is(this)) {
- var thisHeight = $(this).height();
- if (options.forcePlaceholderSize) {
- placeholder.height(draggingHeight);
- }
- // Check if $(this) is bigger than the draggable. If it is, we have to define a dead zone to prevent flickering
- if (thisHeight > draggingHeight) {
- // Dead zone?
- var deadZone = thisHeight - draggingHeight;
- var offsetTop = $(this).offset().top;
- if (placeholder.index() < $(this).index() &&
- e.originalEvent.pageY < offsetTop + deadZone) {
- return false;
- }
- if (placeholder.index() > $(this).index() &&
- e.originalEvent.pageY > offsetTop + thisHeight - deadZone) {
- return false;
- }
- }
- dragging.hide();
- if (placeholder.index() < $(this).index()) {
- $(this).after(placeholder);
- } else {
- $(this).before(placeholder);
- }
- placeholders.not(placeholder).detach();
- } else {
- if (!placeholders.is(this) && !$(this).children(options.items).length) {
- placeholders.detach();
- $(this).append(placeholder);
- }
- }
- return false;
- });
- });
- };
- sortable.destroy = function(sortable) {
- _destroySortable(sortable);
- };
- sortable.enable = function(sortable) {
- _enableSortable(sortable);
- };
- sortable.disable = function(sortable) {
- _disableSortable(sortable);
- };
- $.fn.sortable = function(options) {
- return sortable(this, options);
- };
- return sortable;
- }));
|