/**
 * jQuery Select2 v4.x Sortable
 * - enable select2 to be sortable via normal select element
 * 
 * author      : Mouad (MD Creative) & Vafour
 * inspired by : jQuery Chosen Sortable (https://github.com/mrhenry/jquery-chosen-sortable)
 * License     : MIT
 */

(function($){
	$.fn.extend({
		select2SortableOrder: function(){
			var $this = this.filter('[multiple]');

			$this.each(function(){
				var $select  = $(this);

				// skip elements not select2-ed
				if(typeof($select.data('select2')) !== 'object'){
					return false;
				}

				var $select2 = $select.siblings('.select2-container'),
					unselected = [],
					sorted;

				$select.find('option').each(function(){
					!this.selected && unselected.push(this);
				});

				sorted = $($select2.find('ul.select2-selection__rendered li:not(.select2-search)').map( function() {
					if (!this) {
						return undefined;
					}
					var id = $(this).data('data').id;
					return $select.find('option[value="' + id + '"]')[0];
				}));

				sorted.push.apply(sorted, unselected);
				$select.children().remove();
				$select.append(sorted);
			});

			return $this;
		},
		select2Sortable: function(){
			var args         = Array.prototype.slice.call(arguments, 0);
				$this        = this.filter('[multiple]'),
				validMethods = ['destroy'];

			if(args.length === 0 || typeof(args[0]) === 'object')
			{
				var defaultOptions = {
					bindOrder       : 'formSubmit', // or sortableStop
					sortableOptions : {
						placeholder : 'ui-state-highlight',
						items       : 'li:not(.select2-search)',
						tolerance   : 'pointer'
					}
				};
				var options = $.extend(defaultOptions, args[0]);

				// Init select2 only if not already initialized to prevent select2 configuration loss
				if(typeof($this.data('select2')) !== 'object'){
					$this.select2();
				}

				$this.each(function(){
					var $select  = $(this),
						$select2choices = $select.siblings('.select2-container').find('ul.select2-selection__rendered');

					// Init jQuery UI Sortable
					$select2choices.sortable(options.sortableOptions);

					switch(options.bindOrder){
						case 'sortableStop':
							// apply options ordering in sortstop event
							$select2choices.on("sortstop.select2sortable", function( event, ui ) {
								$select.select2SortableOrder();
							});
							$select.on('change', function(e){
								$(this).select2SortableOrder();
							});
							break;
						default:
							// apply options ordering in form submit
							$select.closest('form').off('submit.select2sortable').on('submit.select2sortable', function(){
								$select.select2SortableOrder();
							});
					}

				});
			}
			else if(typeof(args[0] === 'string'))
			{
				if($.inArray(args[0], validMethods) == -1)
				{
					throw "Unknown method: " + args[0];
				}
				if(args[0] === 'destroy')
				{
					$this.select2SortableDestroy();
				}
			}
			return $this;
		},
		select2SortableDestroy: function(){
			var $this = this.filter('[multiple]');
			$this.each(function(){
				var $select         = $(this),
					$select2choices = $select.parent().find('ul.select2-selection__rendered');

				// unbind form submit event
				$select.closest('form').off('submit.select2sortable');

				// unbind sortstop event
				$select2choices.off("sortstop.select2sortable");

				// destroy select2Sortable
				$select2choices.sortable('destroy');
			});
			return $this;
		}
	});
}(jQuery));
