Bug 45485 - Drag&Drop скрам-карточек
git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1253 6955db30-a419-402b-8a0d-67ecbb4d7f56master
parent
9afd15d535
commit
96aaa3e81f
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
|
||||
Простая JS-библиотека для организации Drag&Drop
|
||||
|
||||
Использование:
|
||||
dragObject = new DragObject(element); // то, что тащим
|
||||
dropTarget = new DropTarget(element); // то, куда тащим
|
||||
|
||||
Обработчики DropTarget:
|
||||
* dropTarget.canAccept(DragObject) -> boolean
|
||||
Вернуть true, если эта цель может принять этот DragObject
|
||||
* dropTarget.onAccept(DragObject, pos = { x: int, y: int })
|
||||
Объект перетаскивают на эту цель и отпускают
|
||||
x, y - относительные цели координаты, в которых объект отпущен
|
||||
* dropTarget.onEnter()
|
||||
Объект приносят на этой цель
|
||||
* dropTarget.onLeave()
|
||||
Объект уносят с этой цели
|
||||
* dropTarget.onMove(pos = { x: int, y: int })
|
||||
Объект таскают по цели
|
||||
x, y - относительные цели координаты, в которых объект находится
|
||||
|
||||
Обработчики DragObject:
|
||||
* dragObject.onDragStart(offset = { x: int, y: int })
|
||||
Объект начинают перетаскивать
|
||||
x, y - относительные координаты, за которые пользователь взял объект мышкой
|
||||
* dragObject.onDragMove(x, y)
|
||||
Объект перетаскивают
|
||||
x, y - абсолютные координаты нахождения объекта
|
||||
* dragObject.onDragSuccess(DropTarget, pos = { x: int, y: int })
|
||||
Объект принят целью
|
||||
x, y - относительные цели координаты, в которых объект отпущен
|
||||
* dragObject.onDragFail
|
||||
Объект не принят ни одной целью
|
||||
|
||||
Соответственно, все эти обработчики можно переопределять. Так и работаем.
|
||||
Можно даже унаследоваться от класса и написать обработчики прототипами:
|
||||
MyDropTarget.prototype = new DropTarget();
|
||||
MyDropTarget.prototype.onAccept = function(obj, pos) {...};
|
||||
|
||||
*/
|
||||
|
||||
var DragMaster = (function()
|
||||
{
|
||||
var dragObject;
|
||||
var mouseDownAt;
|
||||
var currentDropTarget;
|
||||
|
||||
var usePageX = (function()
|
||||
{
|
||||
var m = navigator.userAgent.match(/Opera.([\d\.]+)/);
|
||||
var mv;
|
||||
if (m && (mv = navigator.userAgent.match(/Version\/([\d\.]+)/)))
|
||||
m = mv;
|
||||
var sf = navigator.userAgent.match(/Safari\//) &&
|
||||
!navigator.userAgent.match(/Chrome/);
|
||||
if (sf || m && parseFloat(m[1]) < 10.5)
|
||||
return true;
|
||||
return false;
|
||||
})();
|
||||
|
||||
function fixEvent(e)
|
||||
{
|
||||
// получить объект событие для IE
|
||||
e = e || window.event;
|
||||
|
||||
// добавить pageX/pageY для IE
|
||||
if (e.pageX == null && e.clientX != null)
|
||||
{
|
||||
var html = document.documentElement;
|
||||
var body = document.body;
|
||||
e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
|
||||
e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
|
||||
}
|
||||
|
||||
// правильный pageX/pageY
|
||||
if (usePageX)
|
||||
{
|
||||
e.pX = e.pageX;
|
||||
e.pY = e.pageY;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.pX = e.clientX;
|
||||
e.pY = e.clientY;
|
||||
}
|
||||
|
||||
// добавить which для IE
|
||||
if (!e.which && e.button)
|
||||
e.which = e.button & 1 ? 1 : ( e.button & 2 ? 3 : ( e.button & 4 ? 2 : 0 ) );
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
function getOffset(elem)
|
||||
{
|
||||
if (elem.getBoundingClientRect)
|
||||
return getOffsetRect(elem);
|
||||
else
|
||||
return getOffsetSum(elem);
|
||||
};
|
||||
|
||||
function getOffsetRect(elem)
|
||||
{
|
||||
var box = elem.getBoundingClientRect();
|
||||
|
||||
var body = document.body;
|
||||
var docElem = document.documentElement;
|
||||
|
||||
var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
|
||||
var clientTop = docElem.clientTop || body.clientTop || 0;
|
||||
var clientLeft = docElem.clientLeft || body.clientLeft || 0;
|
||||
var top = box.top + scrollTop - clientTop;
|
||||
var left = box.left + scrollLeft - clientLeft;
|
||||
|
||||
return { top: Math.round(top), left: Math.round(left) };
|
||||
};
|
||||
|
||||
function getOffsetSum(elem)
|
||||
{
|
||||
var top = 0, left = 0;
|
||||
while(elem)
|
||||
{
|
||||
top = top + parseInt(elem.offsetTop);
|
||||
left = left + parseInt(elem.offsetLeft);
|
||||
elem = elem.offsetParent;
|
||||
}
|
||||
return { top: top, left: left };
|
||||
};
|
||||
|
||||
function mouseDown(e)
|
||||
{
|
||||
e = fixEvent(e);
|
||||
if (e.which != 1)
|
||||
return;
|
||||
mouseDownAt = { x: e.pageX, y: e.pageY, element: this };
|
||||
addDocumentEventHandlers();
|
||||
return false;
|
||||
};
|
||||
|
||||
function mouseMove(e)
|
||||
{
|
||||
e = fixEvent(e);
|
||||
|
||||
// (1)
|
||||
if (mouseDownAt)
|
||||
{
|
||||
if (Math.abs(mouseDownAt.x-e.pageX) < 5 &&
|
||||
Math.abs(mouseDownAt.y-e.pageY) < 5)
|
||||
return false;
|
||||
// Начать перенос
|
||||
var elem = mouseDownAt.element;
|
||||
// текущий объект для переноса
|
||||
dragObject = elem.dragObject;
|
||||
|
||||
// запомнить, с каких относительных координат начался перенос
|
||||
var mouseOffset = getMouseOffset(elem, mouseDownAt.x, mouseDownAt.y);
|
||||
mouseDownAt = null; // запомненное значение больше не нужно, сдвиг уже вычислен
|
||||
|
||||
dragObject.dragStart(mouseOffset); // начали
|
||||
}
|
||||
|
||||
// (2)
|
||||
dragObject.dragMove(e.pageX, e.pageY);
|
||||
dragObject.element._moved = true;
|
||||
|
||||
// (3)
|
||||
var newTarget = getCurrentTarget(e);
|
||||
|
||||
// (4)
|
||||
if (currentDropTarget != newTarget)
|
||||
{
|
||||
if (currentDropTarget && currentDropTarget.onLeave)
|
||||
currentDropTarget.onLeave();
|
||||
if (newTarget && newTarget.onEnter)
|
||||
newTarget.onEnter();
|
||||
currentDropTarget = newTarget;
|
||||
}
|
||||
if (currentDropTarget && currentDropTarget.onMove)
|
||||
currentDropTarget.onMove(getMouseOffset(currentDropTarget.element, e.pX, e.pY));
|
||||
|
||||
// (5)
|
||||
return false;
|
||||
};
|
||||
|
||||
function mouseUp(e)
|
||||
{
|
||||
e = fixEvent(e);
|
||||
|
||||
if (!dragObject) // (1)
|
||||
mouseDownAt = null;
|
||||
else
|
||||
{
|
||||
// (2)
|
||||
if (currentDropTarget)
|
||||
{
|
||||
var pos = getMouseOffset(currentDropTarget.element, e.pX, e.pY);
|
||||
currentDropTarget.accept(dragObject, pos);
|
||||
dragObject.dragSuccess(currentDropTarget, pos);
|
||||
}
|
||||
else
|
||||
dragObject.dragFail();
|
||||
dragObject = null;
|
||||
}
|
||||
|
||||
// (3)
|
||||
removeDocumentEventHandlers();
|
||||
};
|
||||
|
||||
function getMouseOffset(target, x, y)
|
||||
{
|
||||
var docPos = getOffset(target);
|
||||
return { x: x-docPos.left, y: y-docPos.top };
|
||||
};
|
||||
|
||||
function getCurrentTarget(e)
|
||||
{
|
||||
// спрятать объект, получить элемент под ним - и тут же показать опять
|
||||
var x = e.pX, y = e.pY;
|
||||
// чтобы не было заметно мигание - максимально снизим время от hide до show
|
||||
dragObject.hide();
|
||||
var elem = document.elementFromPoint(x, y);
|
||||
dragObject.show();
|
||||
|
||||
// найти самую вложенную dropTarget
|
||||
while (elem)
|
||||
{
|
||||
// которая может принять dragObject
|
||||
if (elem.dropTarget && (!elem.dropTarget.canAccept || elem.dropTarget.canAccept(dragObject)))
|
||||
return elem.dropTarget;
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
|
||||
// dropTarget не нашли
|
||||
return null;
|
||||
};
|
||||
|
||||
function addDocumentEventHandlers()
|
||||
{
|
||||
document.onmousemove = mouseMove;
|
||||
document.onmouseup = mouseUp;
|
||||
document.ondragstart = document.body.onselectstart = function() { return false };
|
||||
};
|
||||
|
||||
function removeDocumentEventHandlers()
|
||||
{
|
||||
document.onmousemove =
|
||||
document.onmouseup =
|
||||
document.ondragstart =
|
||||
document.body.onselectstart = null;
|
||||
};
|
||||
|
||||
return {
|
||||
'makeDraggable': function(element)
|
||||
{
|
||||
element.onmousedown = mouseDown;
|
||||
element.onclick = function()
|
||||
{
|
||||
var r = element._moved && true;
|
||||
element._moved = false;
|
||||
return !r;
|
||||
};
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
/* DragObject */
|
||||
|
||||
function DragObject(element)
|
||||
{
|
||||
if (!element)
|
||||
return;
|
||||
element.dragObject = this;
|
||||
DragMaster.makeDraggable(element);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
DragObject.prototype.dragStart = function(offset)
|
||||
{
|
||||
var s = this.element.style;
|
||||
this.rememberPosition = {
|
||||
top: s.top,
|
||||
left: s.left,
|
||||
position: s.position,
|
||||
opacity: s.opacity,
|
||||
};
|
||||
s.position = 'absolute';
|
||||
this.mouseOffset = offset;
|
||||
if (this.onDragStart)
|
||||
this.onDragStart(offset);
|
||||
};
|
||||
|
||||
DragObject.prototype.hide = function()
|
||||
{
|
||||
this.element.style.display = 'none';
|
||||
};
|
||||
|
||||
DragObject.prototype.show = function()
|
||||
{
|
||||
this.element.style.display = '';
|
||||
};
|
||||
|
||||
DragObject.prototype.dragMove = function(x, y)
|
||||
{
|
||||
this.element.style.top = y - this.mouseOffset.y + 'px';
|
||||
this.element.style.left = x - this.mouseOffset.x + 'px';
|
||||
if (this.onDragMove)
|
||||
this.onDragMove(x, y);
|
||||
};
|
||||
|
||||
DragObject.prototype.dragSuccess = function(dropTarget, pos)
|
||||
{
|
||||
this.restorePosition();
|
||||
if (this.onDragSuccess)
|
||||
this.onDragSuccess(dropTarget, pos);
|
||||
};
|
||||
|
||||
DragObject.prototype.restorePosition = function()
|
||||
{
|
||||
var s = this.element.style;
|
||||
for (var i in this.rememberPosition)
|
||||
s[i] = this.rememberPosition[i];
|
||||
};
|
||||
|
||||
DragObject.prototype.dragFail = function()
|
||||
{
|
||||
this.restorePosition()
|
||||
if (this.onDragFail)
|
||||
this.onDragFail();
|
||||
};
|
||||
|
||||
/* DropTarget */
|
||||
|
||||
function DropTarget(element)
|
||||
{
|
||||
if (!element)
|
||||
return;
|
||||
element.dropTarget = this;
|
||||
this.element = element;
|
||||
};
|
||||
|
||||
DropTarget.prototype.accept = function(dragObject, pos)
|
||||
{
|
||||
if (this.onAccept)
|
||||
this.onAccept(dragObject, pos);
|
||||
if (this.onLeave)
|
||||
this.onLeave();
|
||||
};
|
|
@ -0,0 +1,411 @@
|
|||
var pressedButton;
|
||||
var pasteMode = false;
|
||||
var ctrl = false;
|
||||
var highlitCard;
|
||||
var selectedcards = {};
|
||||
var cuttedcards = [];
|
||||
var cuttedids = [];
|
||||
function addNewCards()
|
||||
{
|
||||
for (var i = idlist.length-1; i >= 0; i--)
|
||||
{
|
||||
if (idlist[i] === '')
|
||||
idlist.pop();
|
||||
else
|
||||
break;
|
||||
}
|
||||
var val = document.getElementById('addbugs').value;
|
||||
var re = /(\d+)/g;
|
||||
var m;
|
||||
while (m = re.exec(val))
|
||||
idlist.push(m[1]);
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function addEmptyPage()
|
||||
{
|
||||
for (var i = 0; i < nr*nc; i++)
|
||||
idlist.push('');
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function addNewIfEnter(ev)
|
||||
{
|
||||
if (ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
addNewCards();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function resetAll()
|
||||
{
|
||||
if (pressedButton)
|
||||
{
|
||||
buttonHandler(null, pressedButton);
|
||||
pressedButton = null;
|
||||
}
|
||||
deselectAll();
|
||||
stopPaste();
|
||||
return true;
|
||||
}
|
||||
function deselectAll()
|
||||
{
|
||||
for (var i in selectedcards)
|
||||
{
|
||||
var e = document.getElementById('cardtd_'+i);
|
||||
e.className = 'cardtd';
|
||||
}
|
||||
selectedcards = {};
|
||||
}
|
||||
function guessButton(ev)
|
||||
{
|
||||
exFixEvent(ev);
|
||||
var t;
|
||||
if (t = searchButtonTarget(ev))
|
||||
return buttonHandler(ev, t);
|
||||
return true;
|
||||
}
|
||||
function buttonHandler(ev, target)
|
||||
{
|
||||
if (!pressedButton)
|
||||
{
|
||||
pressedButton = target;
|
||||
target.style.borderStyle = 'inset';
|
||||
target.style.padding = '4px 2px 2px 4px';
|
||||
}
|
||||
else if (pressedButton == target)
|
||||
{
|
||||
target.style.borderStyle = 'outset';
|
||||
target.style.padding = '3px';
|
||||
pressedButton = null;
|
||||
if (target.id == 'btn_cut')
|
||||
deleteSelectedCards(true);
|
||||
else if (target.id == 'btn_delete')
|
||||
deleteSelectedCards(false);
|
||||
else if (target.id == 'btn_paste')
|
||||
pasteCards();
|
||||
else if (target.id == 'btn_paste_beg')
|
||||
doPasteCards(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function selectCard(ev, target)
|
||||
{
|
||||
var selectedcards_empty = 2;
|
||||
for (var i in selectedcards)
|
||||
{
|
||||
if (i == target.id.substr(7))
|
||||
selectedcards_empty = 1;
|
||||
else
|
||||
{
|
||||
selectedcards_empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pasteMode)
|
||||
{
|
||||
doPasteCards(id_to_coord(target.id)+1);
|
||||
return true;
|
||||
}
|
||||
else if (ctrl || selectedcards_empty)
|
||||
{
|
||||
if (selectedcards_empty == 1)
|
||||
return true;
|
||||
var issel = target.className == 'cardtd selected';
|
||||
target.className = issel ? 'cardtd' : 'cardtd selected';
|
||||
if (issel)
|
||||
delete selectedcards[target.id.substr(7)];
|
||||
else
|
||||
selectedcards[target.id.substr(7)] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function highlightCard(ev)
|
||||
{
|
||||
if (pasteMode)
|
||||
{
|
||||
exFixEvent(ev);
|
||||
var t = searchCardTarget(ev);
|
||||
t.className = 'cardtd highlight';
|
||||
highlitCard = t;
|
||||
}
|
||||
}
|
||||
function unlightCard(ev)
|
||||
{
|
||||
if (pasteMode)
|
||||
{
|
||||
exFixEvent(ev);
|
||||
var t = searchCardTarget(ev);
|
||||
t.className = 'cardtd';
|
||||
}
|
||||
}
|
||||
function to_coord(i)
|
||||
{
|
||||
var to_k = Math.floor(i / nr / nc);
|
||||
var to_i = Math.floor((i / nc) % nr);
|
||||
var to_j = Math.floor(i % nc);
|
||||
return [to_k, to_i, to_j];
|
||||
}
|
||||
function id_to_coord(id)
|
||||
{
|
||||
var m = /(\d+)_(\d+)_(\d+)/.exec(id.substr(7));
|
||||
return (parseInt(m[1])*nr+parseInt(m[2]))*nc+parseInt(m[3]);
|
||||
}
|
||||
function deleteSelectedCards(cut)
|
||||
{
|
||||
var shift = 0;
|
||||
var coord = 0;
|
||||
var n = nr * nc * np;
|
||||
if (cut)
|
||||
{
|
||||
cuttedcards = [];
|
||||
cuttedids = [];
|
||||
}
|
||||
for (var k = 0; k < np; k++)
|
||||
{
|
||||
for (var i = 0; i < nr; i++)
|
||||
{
|
||||
for (var j = 0; j < nc; j++, coord++)
|
||||
{
|
||||
var s = selectedcards[k+'_'+i+'_'+j];
|
||||
var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
|
||||
if (s)
|
||||
{
|
||||
if (cut)
|
||||
{
|
||||
cuttedcards.push(e.innerHTML);
|
||||
cuttedids.push(idlist[coord]);
|
||||
}
|
||||
shift++;
|
||||
}
|
||||
else if (shift > 0)
|
||||
{
|
||||
var to = to_coord(coord-shift);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML = e.innerHTML;
|
||||
idlist[coord-shift] = idlist[coord];
|
||||
}
|
||||
else if (coord + shift < n)
|
||||
continue;
|
||||
// во всех трёх случаях - если выделена для
|
||||
// удаления, если перемещена в другую, или
|
||||
// если находится в конце - очищаем
|
||||
e.innerHTML = emptycell;
|
||||
idlist[coord] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cut && cuttedids.length)
|
||||
document.getElementById('cut_status').innerHTML = 'Вырезано '+cuttedids.length+' карточек.';
|
||||
deselectAll();
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
}
|
||||
function deleteAllCards()
|
||||
{
|
||||
idlist = [];
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('pages').innerHTML = '';
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function pasteCards()
|
||||
{
|
||||
if (!pasteMode)
|
||||
{
|
||||
if (!cuttedids.length)
|
||||
{
|
||||
alert('Сначала выделите и вырежьте какие-нибудь карточки!');
|
||||
return;
|
||||
}
|
||||
alert('Кликните на карточку, после которой нужно вставить вырезанное, либо на кнопку "В начало".');
|
||||
deselectAll();
|
||||
pasteMode = true;
|
||||
document.getElementById('btn_paste_beg').style.display = '';
|
||||
}
|
||||
}
|
||||
function doPasteCards(coord)
|
||||
{
|
||||
stopPaste();
|
||||
var nx = cuttedids.length;
|
||||
if (nx <= 0)
|
||||
return;
|
||||
var n = nr * nc * np;
|
||||
var from, to;
|
||||
for (var i = n-nx-1; i >= coord; i--)
|
||||
{
|
||||
from = to_coord(i);
|
||||
to = to_coord(i+nx);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML =
|
||||
document.getElementById('cardtd_'+from[0]+'_'+from[1]+'_'+from[2]).innerHTML;
|
||||
idlist[i+nx] = idlist[i];
|
||||
}
|
||||
for (var i = 0; i < nx; i++)
|
||||
{
|
||||
to = to_coord(i+coord);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML = cuttedcards[i];
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).className = 'cardtd selected';
|
||||
idlist[i+coord] = cuttedids[i];
|
||||
selectedcards[to[0]+'_'+to[1]+'_'+to[2]] = true;
|
||||
}
|
||||
cuttedids = [];
|
||||
cuttedcards = [];
|
||||
document.getElementById('cut_status').innerHTML = '';
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
}
|
||||
function stopPaste()
|
||||
{
|
||||
if (highlitCard)
|
||||
highlitCard.className = 'cardtd';
|
||||
document.getElementById('btn_paste_beg').style.display = 'none';
|
||||
pasteMode = false;
|
||||
}
|
||||
function ctrlDown(ev)
|
||||
{
|
||||
if (ev.keyCode == 17)
|
||||
{
|
||||
ctrl = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function ctrlUp(ev)
|
||||
{
|
||||
if (ev.keyCode == 17)
|
||||
{
|
||||
ctrl = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function searchCardTarget(e)
|
||||
{
|
||||
var nt = e._target, i;
|
||||
while (nt && (!nt.attributes ||
|
||||
!(i = nt.attributes.getNamedItem('id')) ||
|
||||
i.value.substr(0, 7) != 'cardtd_'))
|
||||
nt = nt.parentNode;
|
||||
return nt;
|
||||
}
|
||||
function searchButtonTarget(e)
|
||||
{
|
||||
var nt = e._target, i;
|
||||
while (nt && (!nt.attributes ||
|
||||
!(i = nt.attributes.getNamedItem('id')) ||
|
||||
i.value.substr(0, 4) != 'btn_'))
|
||||
nt = nt.parentNode;
|
||||
return nt;
|
||||
}
|
||||
function mouseUpHandler(e)
|
||||
{
|
||||
exFixEvent(e);
|
||||
var card;
|
||||
if (card = searchCardTarget(e))
|
||||
selectCard(e, card);
|
||||
else if (card = searchButtonTarget(e))
|
||||
buttonHandler(e, card);
|
||||
else
|
||||
resetAll();
|
||||
}
|
||||
var CardDragObject = function(e) { DragObject.call(this, e); };
|
||||
CardDragObject.prototype = new DragObject();
|
||||
CardDragObject.prototype.onDragStart = function() {
|
||||
this.tmp = document.createElement('td');
|
||||
this.element.style.opacity = 0.5;
|
||||
this.element.parentNode.insertBefore(this.tmp, this.element);
|
||||
this.element.parentNode.parentNode.parentNode.appendChild(this.element);
|
||||
};
|
||||
CardDragObject.prototype.onDragSuccess = function(target, pos) {
|
||||
this.tmp.parentNode.insertBefore(this.element, this.tmp);
|
||||
this.tmp.parentNode.removeChild(this.tmp);
|
||||
var n = true;
|
||||
for (var i in selectedcards)
|
||||
{
|
||||
n = false;
|
||||
break;
|
||||
}
|
||||
if (n)
|
||||
selectedcards[this.element.id.substr(7)] = true;
|
||||
deleteSelectedCards(true);
|
||||
var to = id_to_coord(target.element.id);
|
||||
var w = target.element.scrollWidth;
|
||||
if (pos.x < w/4 && to > 0)
|
||||
to--;
|
||||
else if (pos.x > w*3/4 && to+1 < np*nr*nc)
|
||||
to++;
|
||||
doPasteCards(to);
|
||||
};
|
||||
CardDragObject.prototype.onDragFail = function() {
|
||||
this.tmp.parentNode.insertBefore(this.element, this.tmp);
|
||||
this.tmp.parentNode.removeChild(this.tmp);
|
||||
};
|
||||
var CardDropTarget = function(e)
|
||||
{
|
||||
DropTarget.call(this, e);
|
||||
this.n = id_to_coord(e.id);
|
||||
};
|
||||
CardDropTarget.prototype = new DropTarget();
|
||||
CardDropTarget.prototype.onLeave = function()
|
||||
{
|
||||
this.element.style.border = '';
|
||||
this.element.className = 'cardtd';
|
||||
};
|
||||
CardDropTarget.prototype.onMove = function(pos)
|
||||
{
|
||||
var w = this.element.scrollWidth;
|
||||
if (pos.x < w/4 && this.n > 0)
|
||||
{
|
||||
this.element.style.borderLeft = '5px solid red';
|
||||
this.element.className = 'cardtd';
|
||||
}
|
||||
else if (pos.x > w*3/4 && this.n+1 < np*nr*nc)
|
||||
{
|
||||
this.element.style.borderRight = '5px solid red';
|
||||
this.element.className = 'cardtd';
|
||||
}
|
||||
else
|
||||
{
|
||||
this.element.style.border = '';
|
||||
this.element.className = 'cardtd highlight';
|
||||
}
|
||||
};
|
||||
var addListener = function() {
|
||||
if (window.addEventListener) {
|
||||
return function(el, type, fn) { el.addEventListener(type, fn, false); };
|
||||
} else if (window.attachEvent) {
|
||||
return function(el, type, fn) {
|
||||
var f = function() { return fn.call(el, window.event); };
|
||||
el.attachEvent('on'+type, f);
|
||||
};
|
||||
} else {
|
||||
return function(el, type, fn) { element['on'+type] = fn; }
|
||||
}
|
||||
}();
|
||||
var exFixEvent = function(ev)
|
||||
{
|
||||
if (!ev) var ev = window.event;
|
||||
var t = ev.target;
|
||||
if (!t) t = ev.srcElement;
|
||||
if (t && t.nodeType == 3) t = t.parentNode; // Safari bug
|
||||
ev._target = t;
|
||||
// FIXME можно сюда ещё добавить фиксы из DragDrop::fixEvent
|
||||
}
|
||||
for (var k = 0; k < np; k++)
|
||||
{
|
||||
for (var i = 0; i < nr; i++)
|
||||
{
|
||||
for (var j = 0; j < nc; j++)
|
||||
{
|
||||
var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
|
||||
addListener(e, 'mouseover', highlightCard);
|
||||
addListener(e, 'mouseout', unlightCard);
|
||||
new CardDragObject(e);
|
||||
new CardDropTarget(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
addListener(document, "mousedown", guessButton);
|
||||
addListener(document, "mouseup", mouseUpHandler);
|
||||
addListener(document, "keydown", ctrlDown);
|
||||
addListener(document.getElementById('addbugs'), "keypress", addNewIfEnter);
|
||||
addListener(document, "keyup", ctrlUp);
|
|
@ -6,6 +6,7 @@
|
|||
<head>
|
||||
<title>Печать SCRUM-карточек[% IF searchname || defaultsavename %][% ": " _ (searchname || defaultsavename) | html %][% END %]</title>
|
||||
<link rel="stylesheet" type="text/css" media="print" href="skins/standard/print.css" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<script language="JavaScript" type="text/javascript" src="js/yui/yahoo-dom-event.js"></script>
|
||||
<style type="text/css">
|
||||
.dot { border: 1px dashed black; }
|
||||
|
@ -25,6 +26,7 @@ p { margin: 0.3em 0; }
|
|||
.next { page-break-before: always; }
|
||||
@media screen {
|
||||
.page { margin-top: 16px; }
|
||||
.cardtd { background: white; }
|
||||
.cardtd.selected { background: #E0E0FF; }
|
||||
.cardtd.highlight { background: #FFE0E0; }
|
||||
}
|
||||
|
@ -120,6 +122,7 @@ x
|
|||
|
||||
<p style="clear: both">
|
||||
Выделяйте карточки Ctrl+Click. <span id="cut_status"></span>
|
||||
Карточки можно перетаскивать.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -135,12 +138,16 @@ x
|
|||
<tr>
|
||||
[% FOR bug = row.bugs %][% SET coli = loop.index %]
|
||||
<td class="cardtd" id="cardtd_[% pagei %]_[% rowi %]_[% coli %]">
|
||||
<table class="card" cellspacing="5">
|
||||
[% IF NOT bug %]
|
||||
<table class="card" cellspacing="5">
|
||||
<tr><td> </td></tr>
|
||||
</table>
|
||||
[% ELSIF bug.error %]
|
||||
<table class="card" cellspacing="5">
|
||||
<tr><td><span style="font-size: 200%">[% bug.bug_id %]</span><br />[% bug.error %]</td></tr>
|
||||
</table>
|
||||
[% ELSE %]
|
||||
<table class="card" cellspacing="5">
|
||||
<tr>
|
||||
<td class="dot"><a href="show_bug.cgi?id=[% bug.bug_id %]">[% bug.bug_id | html %]</a></td>
|
||||
<td class="sevpri">[% bug.bug_severity.substr(0, 3) | html %] [% bug.priority | html %]</td>
|
||||
|
@ -158,8 +165,8 @@ x
|
|||
[% ind.${bug.bug_id} = ind.${bug.bug_id}+1 %]
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
[% END %]
|
||||
</td>
|
||||
[% END %]
|
||||
</tr>
|
||||
|
@ -170,313 +177,14 @@ x
|
|||
</form>
|
||||
|
||||
<script language="JavaScript">
|
||||
var pressedButton;
|
||||
var pasteMode = false;
|
||||
var ctrl = false;
|
||||
var highlitCard;
|
||||
var selectedcards = {};
|
||||
var cuttedcards = [];
|
||||
var cuttedids = [];
|
||||
var emptycell = "<table class='card' cellspacing='5'><tr><td> </td></tr></table>";
|
||||
var np = [% pages.size || "0" %];
|
||||
var nr = [% t.rows || "0" %];
|
||||
var nc = [% t.cols || "0" %];
|
||||
var idlist = [ [% idlist_js || "" %] ];
|
||||
function addNewCards()
|
||||
{
|
||||
for (var i = idlist.length-1; i >= 0; i--)
|
||||
{
|
||||
if (idlist[i] === '')
|
||||
idlist.pop();
|
||||
else
|
||||
break;
|
||||
}
|
||||
var val = document.getElementById('addbugs').value;
|
||||
var re = /(\d+)/g;
|
||||
var m;
|
||||
while (m = re.exec(val))
|
||||
idlist.push(m[1]);
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function addEmptyPage()
|
||||
{
|
||||
for (var i = 0; i < nr*nc; i++)
|
||||
idlist.push('');
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function addNewIfEnter(ev, target)
|
||||
{
|
||||
if (ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
addNewCards();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function resetAll()
|
||||
{
|
||||
if (pressedButton)
|
||||
{
|
||||
buttonStyle(null, pressedButton);
|
||||
pressedButton = null;
|
||||
}
|
||||
deselectAll();
|
||||
stopPaste();
|
||||
return true;
|
||||
}
|
||||
function deselectAll()
|
||||
{
|
||||
for (var i in selectedcards)
|
||||
{
|
||||
var e = document.getElementById('cardtd_'+i);
|
||||
e.className = 'cardtd';
|
||||
}
|
||||
selectedcards = {};
|
||||
}
|
||||
function buttonStyle(ev, target)
|
||||
{
|
||||
if (!pressedButton)
|
||||
{
|
||||
pressedButton = target;
|
||||
target.style.borderStyle = 'inset';
|
||||
target.style.padding = '4px 2px 2px 4px';
|
||||
}
|
||||
else if (pressedButton == target)
|
||||
{
|
||||
target.style.borderStyle = 'outset';
|
||||
target.style.padding = '3px';
|
||||
pressedButton = null;
|
||||
if (target.id == 'btn_cut')
|
||||
deleteSelectedCards(true);
|
||||
else if (target.id == 'btn_delete')
|
||||
deleteSelectedCards(false);
|
||||
else if (target.id == 'btn_paste')
|
||||
pasteCards();
|
||||
else if (target.id == 'btn_paste_beg')
|
||||
doPasteCards(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function selectCard(ev, target)
|
||||
{
|
||||
if (pasteMode)
|
||||
{
|
||||
var m = /(\d+)_(\d+)_(\d+)/.exec(target.id.substr(7));
|
||||
doPasteCards(parseInt(m[1])*nr*nc+parseInt(m[2])*nc+parseInt(m[3])+1);
|
||||
return true;
|
||||
}
|
||||
else if (ctrl)
|
||||
{
|
||||
var issel = target.className == 'cardtd selected';
|
||||
target.className = issel ? 'cardtd' : 'cardtd selected';
|
||||
if (issel)
|
||||
delete selectedcards[target.id.substr(7)];
|
||||
else
|
||||
selectedcards[target.id.substr(7)] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function highlightCard(ev, target)
|
||||
{
|
||||
if (pasteMode)
|
||||
{
|
||||
target.className = 'cardtd highlight';
|
||||
highlitCard = target;
|
||||
}
|
||||
}
|
||||
function unlightCard(ev, target)
|
||||
{
|
||||
if (pasteMode)
|
||||
target.className = 'cardtd';
|
||||
}
|
||||
function to_coord(i)
|
||||
{
|
||||
var to_k = Math.floor(i / nr / nc);
|
||||
var to_i = Math.floor((i / nc) % nr);
|
||||
var to_j = Math.floor(i % nc);
|
||||
return [to_k, to_i, to_j];
|
||||
}
|
||||
function deleteSelectedCards(cut)
|
||||
{
|
||||
var shift = 0;
|
||||
var coord = 0;
|
||||
var n = nr * nc * np;
|
||||
if (cut)
|
||||
{
|
||||
cuttedcards = [];
|
||||
cuttedids = [];
|
||||
}
|
||||
for (var k = 0; k < np; k++)
|
||||
{
|
||||
for (var i = 0; i < nr; i++)
|
||||
{
|
||||
for (var j = 0; j < nc; j++, coord++)
|
||||
{
|
||||
var s = selectedcards[k+'_'+i+'_'+j];
|
||||
var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
|
||||
if (s)
|
||||
{
|
||||
if (cut)
|
||||
{
|
||||
cuttedcards.push(e.innerHTML);
|
||||
cuttedids.push(idlist[coord]);
|
||||
}
|
||||
shift++;
|
||||
}
|
||||
else if (shift > 0)
|
||||
{
|
||||
var to = to_coord(coord-shift);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML = e.innerHTML;
|
||||
idlist[coord-shift] = idlist[coord];
|
||||
}
|
||||
else if (coord + shift < n)
|
||||
continue;
|
||||
[%# во всех трёх случаях - если выделена для
|
||||
# удаления, если перемещена в другую, или
|
||||
# если находится в конце - очищаем %]
|
||||
e.innerHTML = emptycell;
|
||||
idlist[coord] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cut && cuttedids.length)
|
||||
document.getElementById('cut_status').innerHTML = 'Вырезано '+cuttedids.length+' карточек.';
|
||||
deselectAll();
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
}
|
||||
function deleteAllCards()
|
||||
{
|
||||
idlist = [];
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
document.getElementById('pages').innerHTML = '';
|
||||
document.getElementById('scrumform').submit();
|
||||
}
|
||||
function pasteCards()
|
||||
{
|
||||
if (!pasteMode)
|
||||
{
|
||||
if (!cuttedids.length)
|
||||
{
|
||||
alert('Сначала выделите и вырежьте какие-нибудь карточки!');
|
||||
return;
|
||||
}
|
||||
alert('Кликните на карточку, после которой нужно вставить вырезанное, либо на кнопку "В начало".');
|
||||
deselectAll();
|
||||
pasteMode = true;
|
||||
document.getElementById('btn_paste_beg').style.display = '';
|
||||
}
|
||||
}
|
||||
function doPasteCards(coord)
|
||||
{
|
||||
stopPaste();
|
||||
var nx = cuttedids.length;
|
||||
if (nx <= 0)
|
||||
return;
|
||||
var n = nr * nc * np;
|
||||
var from, to;
|
||||
for (var i = n-nx-1; i >= coord; i--)
|
||||
{
|
||||
from = to_coord(i);
|
||||
to = to_coord(i+nx);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML =
|
||||
document.getElementById('cardtd_'+from[0]+'_'+from[1]+'_'+from[2]).innerHTML;
|
||||
idlist[i+nx] = idlist[i];
|
||||
}
|
||||
for (var i = 0; i < nx; i++)
|
||||
{
|
||||
to = to_coord(i+coord);
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).innerHTML = cuttedcards[i];
|
||||
document.getElementById('cardtd_'+to[0]+'_'+to[1]+'_'+to[2]).className = 'cardtd selected';
|
||||
idlist[i+coord] = cuttedids[i];
|
||||
selectedcards[to[0]+'_'+to[1]+'_'+to[2]] = true;
|
||||
}
|
||||
cuttedids = [];
|
||||
cuttedcards = [];
|
||||
document.getElementById('cut_status').innerHTML = '';
|
||||
document.getElementById('idlist_value').value = idlist.join(',');
|
||||
}
|
||||
function stopPaste()
|
||||
{
|
||||
if (highlitCard)
|
||||
highlitCard.className = 'cardtd';
|
||||
document.getElementById('btn_paste_beg').style.display = 'none';
|
||||
pasteMode = false;
|
||||
}
|
||||
function ctrlDown(ev, target)
|
||||
{
|
||||
if (ev.keyCode == 17)
|
||||
{
|
||||
ctrl = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function ctrlUp(ev, target)
|
||||
{
|
||||
if (ev.keyCode == 17)
|
||||
{
|
||||
ctrl = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var exAttachCount = 0;
|
||||
function exAttachHandler(ev, k, i, func)
|
||||
{
|
||||
if (!ev) var ev = window.event;
|
||||
var t = ev.target;
|
||||
if (!t) t = ev.srcElement;
|
||||
if (t.nodeType == 3) t = t.parentNode; // Safari bug
|
||||
var nt = t;
|
||||
while (nt && (!nt[k] || nt[k] != i))
|
||||
nt = nt.parentNode;
|
||||
var st;
|
||||
if (st = func(ev, nt))
|
||||
{
|
||||
if (ev.stopPropagation)
|
||||
ev.stopPropagation();
|
||||
else
|
||||
ev.cancelBubble = true;
|
||||
}
|
||||
return !st;
|
||||
}
|
||||
function exAttach(element, evname, func)
|
||||
{
|
||||
var i = ++exAttachCount;
|
||||
var k = '_exAt'+evname;
|
||||
element[k] = i;
|
||||
YAHOO.util.Event.addListener(element, evname, function(ev) { return exAttachHandler(ev, k, i, func); });
|
||||
}
|
||||
exAttach(document.getElementById('btn_delete'), "mousedown", buttonStyle);
|
||||
exAttach(document.getElementById('btn_delete'), "mouseup", buttonStyle);
|
||||
exAttach(document.getElementById('btn_cut'), "mousedown", buttonStyle);
|
||||
exAttach(document.getElementById('btn_cut'), "mouseup", buttonStyle);
|
||||
exAttach(document.getElementById('btn_paste'), "mousedown", buttonStyle);
|
||||
exAttach(document.getElementById('btn_paste'), "mouseup", buttonStyle);
|
||||
exAttach(document.getElementById('btn_paste_beg'), "mousedown", buttonStyle);
|
||||
exAttach(document.getElementById('btn_paste_beg'), "mouseup", buttonStyle);
|
||||
for (var k = 0; k < np; k++)
|
||||
{
|
||||
for (var i = 0; i < nr; i++)
|
||||
{
|
||||
for (var j = 0; j < nc; j++)
|
||||
{
|
||||
var e = document.getElementById('cardtd_'+k+'_'+i+'_'+j);
|
||||
exAttach(e, "mouseup", selectCard);
|
||||
exAttach(e, "mouseover", highlightCard);
|
||||
exAttach(e, "mouseout", unlightCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
exAttach(document, "mouseup", resetAll);
|
||||
exAttach(document, "keydown", ctrlDown);
|
||||
exAttach(document.getElementById('addbugs'), "keypress", addNewIfEnter);
|
||||
exAttach(document, "keyup", ctrlUp);
|
||||
</script>
|
||||
<script language="JavaScript" src="extensions/custis/js/DragDrop.js"></script>
|
||||
<script language="JavaScript" src="extensions/custis/js/scrumcards.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue