Rewrite board drag and drop with jquery (touch devices, IE, auto-update)
This commit is contained in:
@@ -29,7 +29,7 @@ body {
|
||||
/* links */
|
||||
a {
|
||||
color: #3366CC;
|
||||
border: 1px solid rgba(255, 255, 255, 0);
|
||||
border: none;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
@@ -123,10 +123,6 @@ label {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input[type="number"],
|
||||
input[type="date"],
|
||||
input[type="email"],
|
||||
@@ -574,17 +570,15 @@ td div.over {
|
||||
}
|
||||
|
||||
.draggable-item {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
[draggable] {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[draggable=true]:hover {
|
||||
box-shadow: 0 0 3px #333;
|
||||
.draggable-placeholder {
|
||||
border: 2px dashed #000;
|
||||
background: #fafafa;
|
||||
height: 70px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
tr td.task a,
|
||||
@@ -607,6 +601,8 @@ div.task-title a {
|
||||
|
||||
div.task {
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.task-score {
|
||||
|
||||
@@ -1,259 +1,102 @@
|
||||
(function () {
|
||||
|
||||
function handleItemDragStart(e)
|
||||
var checkInterval = null;
|
||||
|
||||
// Setup the board
|
||||
function board_load_events()
|
||||
{
|
||||
this.style.opacity = '0.4';
|
||||
|
||||
dragSrcItem = this;
|
||||
dragSrcColumn = this.parentNode;
|
||||
|
||||
e.dataTransfer.effectAllowed = 'copy';
|
||||
e.dataTransfer.setData('text/plain', this.innerHTML);
|
||||
}
|
||||
|
||||
function handleItemDragEnd(e)
|
||||
{
|
||||
// Restore styles
|
||||
removeOver();
|
||||
this.style.opacity = '1.0';
|
||||
|
||||
dragSrcColumn = null;
|
||||
dragSrcItem = null;
|
||||
}
|
||||
|
||||
function handleItemDragOver(e)
|
||||
{
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleItemDragEnter(e)
|
||||
{
|
||||
if (dragSrcItem != this) {
|
||||
removeOver();
|
||||
this.classList.add('over');
|
||||
}
|
||||
}
|
||||
|
||||
function handleItemDrop(e)
|
||||
{
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
|
||||
// Drop the element if the item is not the same
|
||||
if (dragSrcItem != this) {
|
||||
|
||||
var position = getItemPosition(this);
|
||||
var item = createItem(e.dataTransfer.getData('text/plain'));
|
||||
|
||||
if (countColumnItems(this.parentNode) == position) {
|
||||
this.parentNode.appendChild(item);
|
||||
}
|
||||
else {
|
||||
this.parentNode.insertBefore(item, this);
|
||||
$(".column").sortable({
|
||||
connectWith: ".column",
|
||||
placeholder: "draggable-placeholder",
|
||||
stop: function(event, ui) {
|
||||
board_save();
|
||||
}
|
||||
});
|
||||
|
||||
dragSrcItem.parentNode.removeChild(dragSrcItem);
|
||||
var interval = parseInt($("#board").attr("data-check-interval"));
|
||||
|
||||
saveBoard();
|
||||
}
|
||||
|
||||
dragSrcColumn = null;
|
||||
dragSrcItem = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function handleColumnDragOver(e)
|
||||
{
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleColumnDragEnter(e)
|
||||
{
|
||||
if (dragSrcColumn != this) {
|
||||
removeOver();
|
||||
this.classList.add('over');
|
||||
if (interval > 0) {
|
||||
checkInterval = window.setInterval(board_check, interval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function handleColumnDrop(e)
|
||||
// Stop events
|
||||
function board_unload_events()
|
||||
{
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
if (e.stopPropagation) e.stopPropagation();
|
||||
|
||||
// Drop the element if the column is not the same
|
||||
if (dragSrcColumn != this) {
|
||||
|
||||
var item = createItem(e.dataTransfer.getData('text/plain'));
|
||||
this.appendChild(item);
|
||||
dragSrcColumn.removeChild(dragSrcItem);
|
||||
|
||||
saveBoard();
|
||||
}
|
||||
|
||||
return false;
|
||||
clearInterval(checkInterval);
|
||||
}
|
||||
|
||||
function saveBoard()
|
||||
// Save and refresh the board
|
||||
function board_save()
|
||||
{
|
||||
var data = [];
|
||||
var projectId = document.getElementById("board").getAttribute("data-project-id");
|
||||
var cols = document.querySelectorAll('.column');
|
||||
var projectId = $("#board").attr("data-project-id");
|
||||
|
||||
[].forEach.call(cols, function(col) {
|
||||
board_unload_events();
|
||||
|
||||
var task_limit = col.getAttribute("data-task-limit");
|
||||
|
||||
if (task_limit != "" && task_limit != "0") {
|
||||
|
||||
task_limit = parseInt(task_limit);
|
||||
|
||||
if (col.children.length > task_limit) {
|
||||
col.classList.add("task-limit-warning");
|
||||
}
|
||||
else {
|
||||
col.classList.remove("task-limit-warning");
|
||||
}
|
||||
|
||||
var counter = document.getElementById("task-number-column-" + col.getAttribute("data-column-id"));
|
||||
if (counter) counter.innerHTML = col.children.length;
|
||||
}
|
||||
|
||||
[].forEach.call(col.children, function(item) {
|
||||
$(".column").each(function() {
|
||||
var columnId = $(this).attr("data-column-id");
|
||||
|
||||
$("#column-" + columnId + " .task").each(function(index) {
|
||||
data.push({
|
||||
"task_id": item.firstElementChild.getAttribute("data-task-id"),
|
||||
"position": getItemPosition(item),
|
||||
"column_id": col.getAttribute("data-column-id")
|
||||
})
|
||||
"task_id": parseInt($(this).attr("data-task-id")),
|
||||
"position": index + 1,
|
||||
"column_id": parseInt(columnId)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "?controller=board&action=save&project_id=" + projectId, true);
|
||||
$.ajax({
|
||||
url: "?controller=board&action=save&project_id=" + projectId,
|
||||
data: {positions: data},
|
||||
type: "POST",
|
||||
success: function(data) {
|
||||
$("#board").remove();
|
||||
$("#main").append(data);
|
||||
board_load_events();
|
||||
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = function(response) {
|
||||
// Check if a board have been changed by someone else
|
||||
function board_check()
|
||||
{
|
||||
var projectId = $("#board").attr("data-project-id");
|
||||
var timestamp = $("#board").attr("data-time");
|
||||
|
||||
if (this.readyState == this.DONE) {
|
||||
try {
|
||||
var response = JSON.parse(this.responseText);
|
||||
|
||||
if (response.result == false) {
|
||||
window.alert('Unable to update the board');
|
||||
}
|
||||
else if (response.refresh == true) {
|
||||
window.location = "?controller=board&action=show&project_id=" + projectId;
|
||||
if (projectId != undefined && timestamp != undefined) {
|
||||
$.ajax({
|
||||
url: "?controller=board&action=check&project_id=" + projectId + "×tamp=" + timestamp,
|
||||
statusCode: {
|
||||
200: function(data) {
|
||||
$("#board").remove();
|
||||
$("#main").append(data);
|
||||
board_unload_events();
|
||||
board_load_events();
|
||||
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function getItemPosition(element)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
while ((element = element.previousSibling) != null) {
|
||||
|
||||
if (element.nodeName == "DIV" && element.className == "draggable-item") {
|
||||
i++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
function countColumnItems(element)
|
||||
{
|
||||
return element.children.length;
|
||||
}
|
||||
|
||||
function createItem(html)
|
||||
{
|
||||
var item = document.createElement("div");
|
||||
item.className = "draggable-item";
|
||||
item.draggable = true;
|
||||
item.innerHTML = html;
|
||||
item.ondragstart = handleItemDragStart;
|
||||
item.ondragend = handleItemDragEnd;
|
||||
item.ondragenter = handleItemDragEnter;
|
||||
item.ondragover = handleItemDragOver;
|
||||
item.ondrop = handleItemDrop;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function removeOver()
|
||||
{
|
||||
// Remove column over
|
||||
[].forEach.call(document.querySelectorAll('.column'), function (col) {
|
||||
col.classList.remove('over');
|
||||
});
|
||||
|
||||
// Remove item over
|
||||
[].forEach.call(document.querySelectorAll('.draggable-item'), function (item) {
|
||||
item.classList.remove('over');
|
||||
});
|
||||
}
|
||||
|
||||
// Drag and drop events
|
||||
|
||||
var dragSrcItem = null;
|
||||
var dragSrcColumn = null;
|
||||
|
||||
var items = document.querySelectorAll('.draggable-item');
|
||||
|
||||
[].forEach.call(items, function(item) {
|
||||
item.addEventListener('dragstart', handleItemDragStart, false);
|
||||
item.addEventListener('dragend', handleItemDragEnd, false);
|
||||
item.addEventListener('dragenter', handleItemDragEnter, false);
|
||||
item.addEventListener('dragover', handleItemDragOver, false);
|
||||
item.addEventListener('drop', handleItemDrop, false);
|
||||
});
|
||||
|
||||
var cols = document.querySelectorAll('.column');
|
||||
|
||||
[].forEach.call(cols, function(col) {
|
||||
col.addEventListener('dragenter', handleColumnDragEnter, false);
|
||||
col.addEventListener('dragover', handleColumnDragOver, false);
|
||||
col.addEventListener('drop', handleColumnDrop, false);
|
||||
});
|
||||
|
||||
[].forEach.call(document.querySelectorAll('[data-task-id]'), function (item) {
|
||||
item.addEventListener('click', function() {
|
||||
window.location.href = '?controller=task&action=show&task_id=' + item.getAttribute('data-task-id');
|
||||
});
|
||||
});
|
||||
|
||||
// Filtering
|
||||
|
||||
// Get the selected user id
|
||||
function getSelectedUserFilter()
|
||||
{
|
||||
var select = document.getElementById("form-user_id");
|
||||
return select.options[select.selectedIndex].value;
|
||||
return $("#form-user_id").val();
|
||||
}
|
||||
|
||||
// Return true if the filter is activated
|
||||
function hasDueDateFilter()
|
||||
{
|
||||
var dateFilter = document.getElementById("filter-due-date");
|
||||
return dateFilter.classList.contains("filter-on");
|
||||
return $("#filter-due-date").hasClass("filter-on");
|
||||
}
|
||||
|
||||
// Apply user or date filter (change tasks opacity)
|
||||
function applyFilter(selectedUserId, filterDueDate)
|
||||
{
|
||||
[].forEach.call(document.querySelectorAll('[data-task-id]'), function (item) {
|
||||
$("[data-task-id]").each(function(index, item) {
|
||||
|
||||
var ownerId = item.getAttribute("data-owner-id");
|
||||
var dueDate = item.getAttribute("data-due-date");
|
||||
@@ -271,22 +114,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
var userFilter = document.getElementById("form-user_id");
|
||||
var dateFilter = document.getElementById("filter-due-date");
|
||||
|
||||
if (userFilter) {
|
||||
userFilter.onchange = function() {
|
||||
// Load filter events
|
||||
function filter_load_events()
|
||||
{
|
||||
$("#form-user_id").change(function() {
|
||||
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (dateFilter) {
|
||||
|
||||
dateFilter.onclick = function(e) {
|
||||
dateFilter.classList.toggle("filter-on");
|
||||
$("#filter-due-date").click(function(e) {
|
||||
$(this).toggleClass("filter-on");
|
||||
applyFilter(getSelectedUserFilter(), hasDueDateFilter());
|
||||
e.preventDefault();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Initialization
|
||||
$(function() {
|
||||
board_load_events();
|
||||
filter_load_events();
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
4
assets/js/jquery-1.11.1.min.js
vendored
Normal file
4
assets/js/jquery-1.11.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
assets/js/jquery-ui-1.10.4.custom.min.js
vendored
Normal file
6
assets/js/jquery-ui-1.10.4.custom.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
assets/js/jquery.ui.touch-punch.min.js
vendored
Normal file
11
assets/js/jquery.ui.touch-punch.min.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* jQuery UI Touch Punch 0.2.3
|
||||
*
|
||||
* Copyright 2011–2014, Dave Furfero
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
*/
|
||||
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
|
||||
Reference in New Issue
Block a user