/**
* Custom Binding for drag and drop library dragula.js.
* Supports copying, dragging, dropping and removing fields
* @module app/custom_bindings/dragula
* @requires knockout
* @requires dragula
* @requires jquery
* @requires app/models/Field
*
* @example
* <div data-bind="dragula: { data: myObservable }">
* //IMPORTANT: The "handle" class is used to determine the drag handle of the field so this is required
* <span class="handle">My draggable item</span>
* </div>
*/
define(['knockout', 'dragula', 'jquery', 'models/field'], function(ko, dragula, $, Field) {
var modalDisplayed = false;
var parent = null;
var published = false;
var drake = dragula([], {
copy: function(el, source) {
return source.classList.contains('source') || source.classList.contains('repeatable-source');
},
accepts: function(el, target, source, sibling) {
return !(contains(el, target)) &&
(target.classList.contains('target') ||
target.classList.contains('child-fields') ||
target.classList.contains('repeatable-target') ||
target.classList.contains('child-fields'));
},
moves: function(el, source, handle, sibling) {
return handle.classList.contains('handle');
},
invalid: function(el, handle) {
return !handle.classList.contains('handle');
},
revertOnSpill: true,
copySortSource: false
}),
dataField = null;
///////////////////////PRIVATE METHODS: START
// http://ejohn.org/blog/comparing-document-position/
function contains(a, b) {
return a.contains ?
a != b && a.contains(b) :
!!(a.compareDocumentPosition(b) & 16);
}
/**
* UNUSED
*/
function updateFieldNumberingFlag(parent, field) {
return;
console.log('updateFieldNumberingFlag', parent, field);
var showNumbering = null;
if (parent && parent.showNumbering) {
if (ko.isObservable(parent.showNumbering) && parent.showNumbering()) {
showNumbering = true;
} else {
showNumbering = false;
}
} else {
showNumbering = false;
}
if (field && ko.isObservable(field.showNumbering)) {
field.showNumbering(showNumbering);
} else {
field.showNumbering = showNumbering;
}
}
///////////////////////PRIVATE METHODS: END
///////////////////////EVENT LISTENERS: START
drake.on('drag', function(el, source) {
var parents = _.uniqBy(ko.contextFor(el).$parents, 'name');
parent = parents[1];
$('.modal').modal('hide');
ko.utils.arrayForEach([].slice.call($('.child-fields'), 0), function(childContainer) {
if (childContainer !== el) {
$(childContainer).css({
'padding-bottom': '2vh'
});
}
});
if (!source.classList.contains('target') && !source.classList.contains('child-fields')) {
ko.postbox.publish('hide-sidebar');
}
$(el).collapse('toggle');
});
drake.on('dragend', function(el, source) {
$(el).removeClass('drag-start');
});
/**
* DRAGULA AFTER DROP CALLBACK
* @param {DOM} el - element that is dragged
* @param {DOM} target - target element, will accept the element
* @param {DOM} source - where the dragged element came from
* @param {DOM} sibling) - the adjacent element of the dragged item
*/
drake.on('drop', function(el, target, source, sibling) {
// console.log(source.classList.contains('source'));
if (source.classList.contains('source')) {
ko.postbox.publish('open-sidebar');
}
// console.log(target.classList);
if (target) {
published = false;
var indexOf = ko.utils.arrayIndexOf;
if (target.classList.contains('target')) {
console.log('a');
var field = ko.dataFor(el),
fieldContext = ko.contextFor(el),
targetData = ko.dataFor(target),
fieldData = ko.utils.domData.get(el, 'field'),
position = 0;
if (fieldData && fieldData.constructor.name !== 'Field') {
console.log('a.1');
ko.postbox.publish('addField', {
index: indexOf(target.children, el),
data: fieldData
});
} else {
console.log('a.2');
if (field.parent) {
console.log('a.2.0');
ko.postbox.publish('removeChildField', {
field: fieldData,
parent: parent,
dragged: true
});
updateFieldNumberingFlag(parent, fieldData);
}
console.log('a.2.1');
updateFieldNumberingFlag(null, fieldData);
ko.postbox.publish('moveField', {
position: indexOf(target.children, el),
data: fieldData
});
}
} else {
var index = indexOf(target.children, el),
fieldData = ko.utils.domData.get(el, 'field'),
parentElement = $(target).prev()[0],
parentData = ko.dataFor(target);
updateFieldNumberingFlag(parentData, fieldData);
console.log('b', fieldData);
if (fieldData.parent && fieldData.parent === parentData.name) {
console.log('b.1');
ko.postbox.publish('moveChildField', {
position: indexOf(target.children, el),
parent: parentData,
field: fieldData
});
} else {
console.log('b.2');
console.log(parentData);
ko.postbox.publish('addChildField', {
targetIndex: indexOf(target.children, el),
parent: parentData,
field: fieldData
});
}
}
target.removeChild(el);
ko.utils.arrayForEach([].slice.call($('.child-fields'), 0), function(childContainer) {
$(childContainer).css({
'padding-bottom': 'initial'
});
});
}
});
/**
* DRAGULA CALL WHEN COPY IS SET TO TRUE
* @param {DOM} clone - element that is cloned
* @param {DOM} original - original item from source
*/
drake.on('cloned', function(clone, original, type) {
ko.utils.domData.set(clone, 'field', ko.utils.domData.get(original, 'field'));
});
/**
* THIS WILL ADD A target-highlight CSS CLASS
* WHICH WILL ADD A DASHED BORDER AND A GREY
* BACKGROUND TO THE CONTAINER
*/
drake.on('over', function(el, container, source) {
if (container !== source) {
$(container).addClass('target-highlight');
}
});
/**
* THIS WILL REMOVE target-highlight CSS CLASS
*/
drake.on('out', function(el, container, source) {
if (container !== source) {
$(container).removeClass('target-highlight');
}
});
///////////////////////EVENT LISTENERS: END
ko.bindingHandlers.dragula = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {};
drake.containers.push(element);
return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindings, viewModel, bindingContext)
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// this will force update to fire
// valueAccessor().data();
ko.unwrap(valueAccessor);
ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext)
}
};
return {
drake: drake
};
});