You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cryptpad/www/kanban/inner.js

551 lines
21 KiB
JavaScript

define([
'jquery',
'json.sortify',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/sframe-app-framework.js',
'/common/common-util.js',
'/common/common-hash.js',
7 years ago
'/common/common-interface.js',
'/common/modes.js',
'/customize/messages.js',
'/kanban/jkanban.js',
'/common/jscolor.js',
'css!/kanban/jkanban.css',
'less!/kanban/app-kanban.less'
], function (
$,
Sortify,
nThen,
SFCommon,
Framework,
Util,
Hash,
7 years ago
UI,
Modes,
7 years ago
Messages)
{
var verbose = function (x) { console.log(x); };
verbose = function () {}; // comment out to enable verbose logging
var COLORS = ['yellow', 'green', 'orange', 'blue', 'red', 'purple', 'cyan', 'lightgreen', 'lightblue'];
var addRemoveItemButton = function (framework, kanban) {
if (!kanban) { return; }
if (framework.isReadOnly() || framework.isLocked()) { return; }
var $container = $(kanban.element);
$container.find('.kanban-remove-item').remove();
$container.find('.kanban-board .kanban-item').each(function (i, el) {
var pos = kanban.findElementPosition(el);
var board = kanban.options.boards.find(function (b) {
return b.id === $(el.parentNode.parentNode).attr('data-id');
});
$('<button>', {
'class': 'kanban-remove-item btn btn-default fa fa-times',
title: Messages.kanban_removeItem
}).click(function (e) {
e.stopPropagation();
UI.confirm(Messages.kanban_removeItemConfirm, function (yes) {
if (!yes) { return; }
board.item.splice(pos, 1);
$(el).remove();
kanban.onChange();
});
}).appendTo($(el));
});
};
// Kanban code
var initKanban = function (framework, boards) {
var defaultBoards = [{
7 years ago
"id": "todo",
"title": Messages.kanban_todo,
7 years ago
"color": "blue",
"item": [{
"title": Messages._getKey('kanban_item', [1])
}, {
"title": Messages._getKey('kanban_item', [2])
}]
7 years ago
}, {
"id": "working",
"title": Messages.kanban_working,
7 years ago
"color": "orange",
"item": [{
"title": Messages._getKey('kanban_item', [3])
7 years ago
}, {
"title": Messages._getKey('kanban_item', [4])
7 years ago
}]
}, {
"id": "done",
"title": Messages.kanban_done,
7 years ago
"color": "green",
"item": [{
"title": Messages._getKey('kanban_item', [5])
7 years ago
}, {
"title": Messages._getKey('kanban_item', [6])
7 years ago
}]
}];
7 years ago
if (!boards) {
verbose("Initializing with default boards content");
boards = defaultBoards;
} else {
verbose("Initializing with boards content " + boards);
}
// Remove any existing elements
$(".kanban-container-outer").remove();
var getInput = function () {
return $('<input>', {
'type': 'text',
'id': 'kanban-edit',
'size': '30'
}).click(function (e) { e.stopPropagation(); });
};
7 years ago
var kanban = new window.jKanban({
element: '#cp-app-kanban-content',
gutter: '5px',
widthBoard: '300px',
buttonContent: '❌',
colors: COLORS,
readOnly: framework.isReadOnly(),
onChange: function () {
verbose("Board object has changed");
framework.localChange();
if (kanban) {
addRemoveItemButton(framework, kanban);
}
},
click: function (el) {
if (framework.isReadOnly() || framework.isLocked()) { return; }
if (kanban.inEditMode) {
$(el).focus();
verbose("An edit is already active");
//return;
}
kanban.inEditMode = true;
$(el).find('button').remove();
var name = $(el).text();
$(el).html('');
var $input = getInput().val(name).appendTo(el).focus();
$input[0].select();
var save = function () {
// Store the value
var name = $input.val();
// Remove the input
$(el).text(name);
// Save the value for the correct board
var board = $(el).closest('.kanban-board').attr("data-id");
var pos = kanban.findElementPosition(el.parentNode);
kanban.getBoardJSON(board).item[pos].title = name;
kanban.onChange();
// Unlock edit mode
kanban.inEditMode = false;
};
$input.blur(save);
$input.keydown(function (e) {
if (e.which === 13) {
e.preventDefault();
e.stopPropagation();
save();
if (!$input.val()) { return; }
if (!$(el).closest('.kanban-item').is(':last-child')) { return; }
$(el).closest('.kanban-board').find('.kanban-title-button.fa-plus').click();
return;
}
if (e.which === 27) {
e.preventDefault();
e.stopPropagation();
$(el).text(name);
kanban.inEditMode = false;
addRemoveItemButton(framework, kanban);
return;
}
});
},
boardTitleClick: function (el, e) {
e.stopPropagation();
if (framework.isReadOnly() || framework.isLocked()) { return; }
if (kanban.inEditMode) {
$(el).focus();
verbose("An edit is already active");
//return;
}
kanban.inEditMode = true;
var name = $(el).text();
$(el).html('');
var $input = getInput().val(name).appendTo(el).focus();
$input[0].select();
var save = function () {
// Store the value
var name = $input.val();
if (!name || !name.trim()) {
return kanban.onChange();
}
// Remove the input
$(el).text(name);
// Save the value for the correct board
var board = $(el.parentNode.parentNode).attr("data-id");
kanban.getBoardJSON(board).title = name;
kanban.onChange();
// Unlock edit mode
kanban.inEditMode = false;
};
$input.blur(save);
$input.keydown(function (e) {
if (e.which === 13) {
e.preventDefault();
e.stopPropagation();
save();
return;
}
if (e.which === 27) {
e.preventDefault();
e.stopPropagation();
$(el).text(name);
kanban.inEditMode = false;
return;
}
});
},
colorClick: function (el, type) {
if (framework.isReadOnly() || framework.isLocked()) { return; }
verbose("on color click");
var boardJSON;
var board;
if (type === "board") {
verbose("board color click");
board = $(el.parentNode).attr("data-id");
boardJSON = kanban.getBoardJSON(board);
} else {
verbose("item color click");
board = $(el.parentNode.parentNode).attr("data-id");
var pos = kanban.findElementPosition(el);
boardJSON = kanban.getBoardJSON(board).item[pos];
}
var onchange = function (colorL) {
var elL = el;
var typeL = type;
var boardJSONL;
var boardL;
if (typeL === "board") {
verbose("board color change");
boardL = $(elL.parentNode).attr("data-id");
boardJSONL = kanban.getBoardJSON(boardL);
} else {
verbose("item color change");
boardL = $(elL.parentNode.parentNode).attr("data-id");
var pos = kanban.findElementPosition(elL);
boardJSONL = kanban.getBoardJSON(boardL).item[pos];
}
var currentColor = boardJSONL.color;
verbose("Current color " + currentColor);
if (currentColor !== colorL.toString()) {
$(elL).removeClass("kanban-header-" + currentColor);
boardJSONL.color = colorL.toString();
kanban.onChange();
}
};
var jscolorL;
el._jscLinkedInstance = undefined;
jscolorL = new window.jscolor(el,{showOnClick: false, onFineChange: onchange, valueElement:undefined});
jscolorL.show();
var currentColor = boardJSON.color;
if (currentColor === undefined) {
currentColor = '';
}
jscolorL.fromString(currentColor);
},
buttonClick: function (el, boardId, e) {
e.stopPropagation();
if (framework.isReadOnly() || framework.isLocked()) { return; }
UI.confirm(Messages.kanban_deleteBoard, function (yes) {
7 years ago
if (!yes) { return; }
verbose("Delete board");
//var boardName = $(el.parentNode.parentNode).attr("data-id");
7 years ago
for (var index in kanban.options.boards) {
if (kanban.options.boards[index].id === boardId) {
break;
}
index++;
}
kanban.options.boards.splice(index, 1);
kanban.removeBoard(boardId);
kanban.onChange();
7 years ago
});
},
addItemClick: function (el) {
if (framework.isReadOnly() || framework.isLocked()) { return; }
if (kanban.inEditMode) {
$(el).focus();
verbose("An edit is already active");
//return;
}
kanban.inEditMode = true;
// create a form to enter element
var boardId = $(el.parentNode.parentNode).attr("data-id");
var $item = $('<div>', {'class': 'kanban-item new-item'});
var $input = getInput().val(name).appendTo($item);
kanban.addForm(boardId, $item[0]);
$input.focus();
var save = function () {
$item.remove();
kanban.inEditMode = false;
if (!$input.val()) { return; }
kanban.addElement(boardId, {
"title": $input.val(),
7 years ago
});
};
$input.blur(save);
$input.keydown(function (e) {
if (e.which === 13) {
e.preventDefault();
e.stopPropagation();
save();
if (!$input.val()) { return; }
$(el).closest('.kanban-board').find('.kanban-title-button.fa-plus').click();
return;
}
if (e.which === 27) {
e.preventDefault();
e.stopPropagation();
$item.remove();
kanban.inEditMode = false;
return;
}
});
},
addItemButton: true,
boards: boards
});
var addBoardDefault = document.getElementById('kanban-addboard');
$(addBoardDefault).attr('title', Messages.kanban_addBoard);
addBoardDefault.addEventListener('click', function () {
if (framework.isReadOnly()) { return; }
var counter = 1;
7 years ago
// Get the new board id
var boardExists = function (b) { return b.id === "board" + counter; };
while (kanban.options.boards.some(boardExists)) { counter++; }
7 years ago
kanban.addBoards([{
"id": "board" + counter,
"title": Messages.kanban_newBoard,
"color": COLORS[Math.floor(Math.random()*COLORS.length)], // random color
7 years ago
"item": [{
"title": Messages._getKey('kanban_item', [1]),
7 years ago
}]
}]);
kanban.onChange();
});
return kanban;
};
var mkHelpMenu = function (framework) {
var $toolbarContainer = $('#cp-app-kanban-container');
$toolbarContainer.prepend(framework._.sfCommon.getBurnAfterReadingWarning());
var helpMenu = framework._.sfCommon.createHelpMenu(['kanban']);
$toolbarContainer.prepend(helpMenu.menu);
framework._.toolbar.$drawer.append(helpMenu.button);
};
// Start of the main loop
var andThen2 = function (framework) {
var kanban;
var $container = $('#cp-app-kanban-content');
mkHelpMenu(framework);
if (framework.isReadOnly()) {
$container.addClass('cp-app-readonly');
} else {
7 years ago
framework.setFileImporter({}, function (content /*, file */) {
var parsed;
try { parsed = JSON.parse(content); }
catch (e) { return void console.error(e); }
return { content: parsed };
});
}
framework.setFileExporter('.json', function () {
return new Blob([JSON.stringify(kanban.getBoardsJSON(), 0, 2)], {
type: 'application/json',
});
});
framework.onEditableChange(function (unlocked) {
if (framework.isReadOnly()) { return; }
if (!kanban) { return; }
if (unlocked) {
addRemoveItemButton(framework, kanban);
kanban.options.readOnly = false;
return void $container.removeClass('cp-app-readonly');
}
kanban.options.readOnly = true;
$container.addClass('cp-app-readonly');
});
var getSelectedElement = function () {
var node = document.getSelection().anchorNode;
return (node.nodeType === 3 ? node.parentNode : node);
};
var getCursor = function () {
if (!kanban || !kanban.inEditMode) { return; }
try {
var el = getSelectedElement();
var input = $(el).is('input') ? el : $(el).find('input')[0];
if (!input) { return; }
var $input = $(input);
var pos;
var $item = $(el).closest('.kanban-item');
if ($item.length) {
pos = kanban.findElementPosition($item[0]);
}
var board = $input.closest('.kanban-board').attr('data-id');
var val = ($input.val && $input.val()) || '';
var start = input.selectionStart;
var end = input.selectionEnd;
var boardEl = kanban.options.boards.find(function (b) {
return b.id === board;
});
var oldVal = ((pos ? boardEl.item[pos] : boardEl) || {}).title;
return {
board: board,
pos: pos,
value: val,
start: start,
end: end,
oldValue: oldVal
};
} catch (e) {
return {};
}
};
var restoreCursor = function (data) {
try {
var boardEl = kanban.options.boards.find(function (b) {
return b.id === data.board;
});
if (!boardEl) { return; }
var $board = $('.kanban-board[data-id="'+data.board+'"');
// Editing a board title...
if (!data.pos && $board.length) {
if (boardEl.title !== data.oldValue) { return; }
$board.find('.kanban-title-board').click();
var $boardInput = $board.find('.kanban-title-board input');
$boardInput.val(data.value);
$boardInput[0].selectionStart = data.start;
$boardInput[0].selectionEnd = data.end;
return;
}
// Editing a deleted board title: abort
if (!data.pos) {
return;
}
// An item was added: add a new item
if (!data.oldValue) {
$board.find('.kanban-title-button.fa-plus').click();
var $newInput = $board.find('.kanban-item:last-child input');
$newInput.val(data.value);
$newInput[0].selectionStart = data.start;
$newInput[0].selectionEnd = data.end;
return;
}
// An item was edited: click on the correct item
var newVal = boardEl.item[data.pos];
if (!newVal || newVal.title !== data.oldValue) { return; }
var $el = $('.kanban-board[data-id="' + data.board + '"]')
.find('.kanban-item:nth-child('+(data.pos + 1)+')');
$el.find('.kanban-item-text').click();
var $input = $el.find('input');
if ($input.length) {
$input.val(data.value);
$input[0].selectionStart = data.start;
$input[0].selectionEnd = data.end;
}
} catch (e) {
return;
}
};
framework.onContentUpdate(function (newContent) {
// Init if needed
if (!kanban) {
kanban = initKanban(framework, (newContent || {}).content);
addRemoveItemButton(framework, kanban);
return;
}
// Need to update the content
verbose("Content should be updated to " + newContent);
var currentContent = kanban.getBoardsJSON();
var remoteContent = newContent.content;
7 years ago
if (Sortify(currentContent) !== Sortify(remoteContent)) {
var cursor = getCursor();
verbose("Content is different.. Applying content");
kanban.setBoards(remoteContent);
kanban.inEditMode = false;
addRemoveItemButton(framework, kanban);
restoreCursor(cursor);
}
});
framework.setContentGetter(function () {
if (!kanban) {
return {
content: []
};
}
var content = kanban.getBoardsJSON();
verbose("Content current value is " + content);
return {
content: content
};
});
7 years ago
framework.onReady(function () {
$("#cp-app-kanban-content").focus();
});
framework.onDefaultContentNeeded(function () {
kanban = initKanban(framework);
});
framework.start();
};
var main = function () {
// var framework;
nThen(function (waitFor) {
// Framework initialization
Framework.create({
toolbarContainer: '#cme_toolbox',
contentContainer: '#cp-app-kanban-editor',
7 years ago
}, waitFor(function (framework) {
andThen2(framework);
}));
});
};
main();
});