/** * Code for drag&drop and/or editing of SCRUM-like cards from Bugzilla * License: Dual-license GPL 3.0+ or MPL 1.1+ * Author: (c) Vitaliy Filippov 2010-2011 */ var pressedButton; var pasteMode = false; var ctrl = false; var highlitCard; var selectedcards = {}; var cuttedcards = []; var cuttedids = []; // Добавить карточки для багов с ID'шниками из поля addbugs 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(); } // Обработчик нажатия Enter на поле addbugs function addNewIfEnter(ev) { if (ev.keyCode == 10 || ev.keyCode == 13) { addNewCards(); return true; } return false; } // Удалить все карточки function deleteAllCards() { idlist = []; document.getElementById('idlist_value').value = idlist.join(','); document.getElementById('pages').innerHTML = ''; document.getElementById('scrumform').submit(); } // Сбросить состояние кнопок, выделение карточек, режим вставки 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 = {}; } // Обработчик, пытающийся по иерархии найти кнопку (id=btn_*), // и вызвать на ней buttonHandler function guessButton(ev) { ev = DragMaster.fixEvent(ev); var t; if (t = searchButtonTarget(ev)) return buttonHandler(ev, t); return true; } // Обработчик mousedown и mouseup на кнопках (id=btn_*) 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 empty = isEmptyHash(selectedcards, target.id.substr(7)); if (pasteMode) { doPasteCards(id_to_coord(target.id)+1); return true; } else if (ctrl || !selectedcards[target.id.substr(7)]) { if (!ctrl) deselectAll(); 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) { ev = DragMaster.fixEvent(ev); var t = searchCardTarget(ev); t.className = 'cardtd highlight'; highlitCard = t; } } // Снятие подсветки карточки в режиме вставки function unlightCard(ev) { if (pasteMode) { ev = DragMaster.fixEvent(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 to_coord_id(i) { return to_coord(i).join('_'); } // Преобразование id элемента (.*лист_строка_столбец.*) в номер 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]); } // Удалить(cut=0)/вырезать(cut=1) выделенные карточки 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 (!e) continue; if (s) { if (cut) { cuttedcards.push(e.innerHTML); cuttedids.push(idlist[coord]); } shift++; } else if (shift > 0) { var to = to_coord_id(coord-shift); document.getElementById('cardtd_'+to).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 pasteCards() { if (!pasteMode) { if (!cuttedids.length) { alert('Сначала выделите и вырежьте какие-нибудь карточки!'); return; } alert('Кликните на карточку, после которой нужно вставить вырезанное, либо на кнопку "В начало".'); deselectAll(); pasteMode = true; document.getElementById('btn_paste_beg').style.display = ''; } } // Вставить вырезанные карточки в позицию coord function doPasteCards(coord) { stopPaste(); var nx = cuttedids.length; if (nx <= 0) return; if (!document.getElementById('cardtd_'+to_coord_id(coord))) { alert('Некуда вставлять'); return; } var n = nr * nc * np; var from, to; for (var i = n-nx-1; i >= coord; i--) { from = to_coord_id(i); to = to_coord_id(i+nx); if (document.getElementById('cardtd_'+from) && document.getElementById('cardtd_'+to)) { document.getElementById('cardtd_'+to).innerHTML = document.getElementById('cardtd_'+from).innerHTML; idlist[i+nx] = idlist[i]; } } for (var i = 0; i < nx; i++) { to = to_coord_id(i+coord); document.getElementById('cardtd_'+to).innerHTML = cuttedcards[i]; document.getElementById('cardtd_'+to).className = 'cardtd selected'; idlist[i+coord] = cuttedids[i]; selectedcards[to] = 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; } // Обработчик keydown/keyup, записывает состояние ctrl function ctrlDown(ev) { if (ev.keyCode == 17) { ctrl = ev.type == 'keydown'; return true; } return false; } // Попробовать найти по иерархии карточку (id=cardtd_*) 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; } // Попробовать найти по иерархии кнопку (id=btn_*) 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; } // Попробовать найти по иерархии что-нибудь кроме карточек и кнопок, // по сути это только и