diff options
Diffstat (limited to 'assets/js/src')
-rw-r--r-- | assets/js/src/analytic.js | 209 | ||||
-rw-r--r-- | assets/js/src/base.js | 284 | ||||
-rw-r--r-- | assets/js/src/board.js | 411 | ||||
-rw-r--r-- | assets/js/src/budget.js | 38 | ||||
-rw-r--r-- | assets/js/src/calendar.js | 152 | ||||
-rw-r--r-- | assets/js/src/dashboard.js | 52 | ||||
-rw-r--r-- | assets/js/src/screenshot.js | 102 | ||||
-rw-r--r-- | assets/js/src/swimlane.js | 90 |
8 files changed, 1338 insertions, 0 deletions
diff --git a/assets/js/src/analytic.js b/assets/js/src/analytic.js new file mode 100644 index 00000000..0912bbcb --- /dev/null +++ b/assets/js/src/analytic.js @@ -0,0 +1,209 @@ + +Kanboard.Analytic = (function() { + + jQuery(document).ready(function() { + + if (Kanboard.Exists("analytic-task-repartition")) { + Kanboard.Analytic.TaskRepartition.Init(); + } + else if (Kanboard.Exists("analytic-user-repartition")) { + Kanboard.Analytic.UserRepartition.Init(); + } + else if (Kanboard.Exists("analytic-cfd")) { + Kanboard.Analytic.CFD.Init(); + } + else if (Kanboard.Exists("analytic-burndown")) { + Kanboard.Analytic.Burndown.Init(); + } + }); + + return {}; + +})(); + +Kanboard.Analytic.Burndown = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels); + }); + } + + function drawGraph(metrics, labels) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", "100%", 380); + var chart = new dimple.chart(svg, series); + + var x = chart.addCategoryAxis("x", labels['day']); + x.addOrderRule("Date"); + + chart.addMeasureAxis("y", labels['score']); + chart.addSeries(null, dimple.plot.line); + + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var row = {}; + var score = parseInt(metrics[i]['score']); + row[labels['day']] = metrics[i]['day']; + row[labels['score']] = score; + series.push(row); + } + + return series; + } + + return { + Init: fetchData + }; + +})(); + +Kanboard.Analytic.CFD = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels, data.columns); + }); + } + + function drawGraph(metrics, labels, columns) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", "100%", 380); + var chart = new dimple.chart(svg, series); + + var x = chart.addCategoryAxis("x", labels['day']); + x.addOrderRule("Date"); + + chart.addMeasureAxis("y", labels['total']); + + var s = chart.addSeries(labels['column'], dimple.plot.area); + s.addOrderRule(columns.reverse()); + + chart.addLegend(10, 10, 500, 30, "left"); + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var row = {}; + row[labels['column']] = metrics[i]['column_title']; + row[labels['day']] = metrics[i]['day']; + row[labels['total']] = metrics[i]['total']; + series.push(row); + } + + return series; + } + + return { + Init: fetchData + }; + +})(); + +Kanboard.Analytic.TaskRepartition = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels); + }); + } + + function drawGraph(metrics, labels) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", "100%", 350); + + var chart = new dimple.chart(svg, series); + chart.addMeasureAxis("p", labels["nb_tasks"]); + var ring = chart.addSeries(labels["column_title"], dimple.plot.pie); + ring.innerRadius = "50%"; + chart.addLegend(0, 0, 100, "100%", "left"); + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var serie = {}; + serie[labels["nb_tasks"]] = metrics[i]["nb_tasks"]; + serie[labels["column_title"]] = metrics[i]["column_title"]; + + series.push(serie); + } + + return series; + } + + return { + Init: fetchData + }; + +})(); + +Kanboard.Analytic.UserRepartition = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels); + }); + } + + function drawGraph(metrics, labels) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", "100%", 350); + + var chart = new dimple.chart(svg, series); + chart.addMeasureAxis("p", labels["nb_tasks"]); + var ring = chart.addSeries(labels["user"], dimple.plot.pie); + ring.innerRadius = "50%"; + chart.addLegend(0, 0, 100, "100%", "left"); + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var serie = {}; + serie[labels["nb_tasks"]] = metrics[i]["nb_tasks"]; + serie[labels["user"]] = metrics[i]["user"]; + + series.push(serie); + } + + return series; + } + + return { + Init: fetchData + }; + +})(); diff --git a/assets/js/src/base.js b/assets/js/src/base.js new file mode 100644 index 00000000..0d46b503 --- /dev/null +++ b/assets/js/src/base.js @@ -0,0 +1,284 @@ +// Common functions +var Kanboard = (function() { + + jQuery(document).ready(function() { + Kanboard.Init(); + }); + + return { + + // Return true if the element#id exists + Exists: function(id) { + if (document.getElementById(id)) { + return true; + } + + return false; + }, + + // Open a popup on a link click + Popover: function(e, callback) { + e.preventDefault(); + e.stopPropagation(); + + var link = e.target.getAttribute("href"); + + if (! link) { + link = e.target.getAttribute("data-href"); + } + + if (link) { + Kanboard.OpenPopover(link, callback); + } + }, + + // Display a popup + OpenPopover: function(link, callback) { + + $.get(link, function(content) { + + $("body").append('<div id="popover-container"><div id="popover-content">' + content + '</div></div>'); + + $("#popover-container").click(function() { + $(this).remove(); + }); + + $("#popover-content").click(function(e) { + e.stopPropagation(); + }); + + $(".close-popover").click(function(e) { + e.preventDefault(); + $('#popover-container').remove(); + }); + + Mousetrap.bind("esc", function() { + $('#popover-container').remove(); + }); + + if (callback) { + callback(); + } + }); + }, + + // Return true if the page is visible + IsVisible: function() { + + var property = ""; + + if (typeof document.hidden !== "undefined") { + property = "visibilityState"; + } else if (typeof document.mozHidden !== "undefined") { + property = "mozVisibilityState"; + } else if (typeof document.msHidden !== "undefined") { + property = "msVisibilityState"; + } else if (typeof document.webkitHidden !== "undefined") { + property = "webkitVisibilityState"; + } + + if (property != "") { + return document[property] == "visible"; + } + + return true; + }, + + // Save preferences in local storage + SetStorageItem: function(key, value) { + if (typeof(Storage) !== "undefined") { + localStorage.setItem(key, value); + } + }, + + GetStorageItem: function(key) { + + if (typeof(Storage) !== "undefined") { + return localStorage.getItem(key); + } + + return ''; + }, + + // Generate Markdown preview + MarkdownPreview: function(e) { + + e.preventDefault(); + + var link = $(this); + var nav = $(this).closest("ul"); + var write = $(".write-area"); + var preview = $(".preview-area"); + var textarea = $("textarea"); + + var request = $.ajax({ + url: "?controller=app&action=preview", + contentType: "application/json", + type: "POST", + processData: false, + dataType: "html", + data: JSON.stringify({ + "text": textarea.val() + }) + }); + + request.done(function(data) { + + nav.find("li").removeClass("form-tab-selected"); + link.parent().addClass("form-tab-selected"); + + preview.find(".markdown").html(data) + preview.css("height", textarea.css("height")); + preview.css("width", textarea.css("width")); + + write.hide(); + preview.show(); + }); + }, + + // Show the Markdown textarea + MarkdownWriter: function(e) { + + e.preventDefault(); + + $(this).closest("ul").find("li").removeClass("form-tab-selected") + $(this).parent().addClass("form-tab-selected"); + + $(".write-area").show(); + $(".preview-area").hide(); + }, + + // Check session and redirect to the login page if not logged + CheckSession: function() { + + if (! $(".form-login").length) { + $.ajax({ + cache: false, + url: $("body").data("status-url"), + statusCode: { + 401: function() { + window.location = $("body").data("login-url"); + } + } + }); + } + }, + + Init: function() { + + // Chosen select + $(".chosen-select").chosen({ + width: "200px", + no_results_text: $(".chosen-select").data("notfound"), + disable_search_threshold: 10 + }); + + // Project select box + $("#board-selector").chosen({ + width: 180, + no_results_text: $("#board-selector").data("notfound") + }); + + $("#board-selector").change(function() { + window.location = $(this).attr("data-board-url").replace(/PROJECT_ID/g, $(this).val()); + }); + + // Check the session every 60s + window.setInterval(Kanboard.CheckSession, 60000); + + // Keyboard shortcuts + Mousetrap.bindGlobal("mod+enter", function() { + $("form").submit(); + }); + + Mousetrap.bind("b", function(e) { + e.preventDefault(); + $('#board-selector').trigger('chosen:open'); + }); + + $.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]); + + Kanboard.InitAfterAjax(); + }, + + InitAfterAjax: function() { + + // Popover + $(document).on("click", ".popover", Kanboard.Popover); + + // Autofocus fields (html5 autofocus works only with page onload) + $("[autofocus]").each(function(index, element) { + $(this).focus(); + }) + + // Datepicker + $(".form-date").datepicker({ + showOtherMonths: true, + selectOtherMonths: true, + dateFormat: 'yy-mm-dd', + constrainInput: false + }); + + // Markdown Preview for textareas + $("#markdown-preview").click(Kanboard.MarkdownPreview); + $("#markdown-write").click(Kanboard.MarkdownWriter); + + // Auto-select input fields + $(".auto-select").focus(function() { + $(this).select(); + }); + + // Dropdown + $(".dropit-submenu").hide(); + $('.dropdown').not(".dropit").dropit({ triggerParentEl : "span" }); + + // Task auto-completion + if ($(".task-autocomplete").length) { + + if ($('.opposite_task_id').val() == '') { + $(".task-autocomplete").parent().find("input[type=submit]").attr('disabled','disabled'); + } + + $(".task-autocomplete").autocomplete({ + source: $(".task-autocomplete").data("search-url"), + minLength: 1, + select: function(event, ui) { + var field = $(".task-autocomplete").data("dst-field"); + $("input[name=" + field + "]").val(ui.item.id); + + $(".task-autocomplete").parent().find("input[type=submit]").removeAttr('disabled'); + } + }); + } + + // Tooltip for column description + $(".column-tooltip").tooltip({ + content: function() { + return '<div class="markdown">' + $(this).attr("title") + '</div>'; + }, + position: { + my: 'left-20 top', + at: 'center bottom+9', + using: function(position, feedback) { + + $(this).css(position); + + var arrow_pos = feedback.target.left + feedback.target.width / 2 - feedback.element.left - 20; + + $("<div>") + .addClass("tooltip-arrow") + .addClass(feedback.vertical) + .addClass(arrow_pos == 0 ? "align-left" : "align-right") + .appendTo(this); + } + } + }); + + // Screenshot + if (Kanboard.Exists("screenshot-zone")) { + Kanboard.Screenshot.Init(); + } + } + }; + +})(); diff --git a/assets/js/src/board.js b/assets/js/src/board.js new file mode 100644 index 00000000..9f93a869 --- /dev/null +++ b/assets/js/src/board.js @@ -0,0 +1,411 @@ +Kanboard.Board = (function() { + + var checkInterval = null; + + function on_popover(e) + { + e.preventDefault(); + e.stopPropagation(); + + Kanboard.Popover(e, Kanboard.InitAfterAjax); + } + + function keyboard_shortcuts() + { + Mousetrap.bind("n", function() { + + Kanboard.OpenPopover( + $("#board").data("task-creation-url"), + Kanboard.InitAfterAjax + ); + }); + + Mousetrap.bind("s", function() { + stack_toggle(); + }); + + Mousetrap.bind("c", function() { + compactview_toggle(); + }); + } + + // Collapse/Expand tasks + function stack_load_events() + { + $(".filter-expand-link").click(function(e) { + e.preventDefault(); + stack_expand(); + Kanboard.SetStorageItem(stack_key(), "expanded"); + }); + + $(".filter-collapse-link").click(function(e) { + e.preventDefault(); + stack_collapse(); + Kanboard.SetStorageItem(stack_key(), "collapsed"); + }); + + stack_show(); + } + + function stack_key() + { + var projectId = $('#board').data('project-id'); + return "board_stacking_" + projectId; + } + + function stack_collapse() + { + $(".filter-collapse").hide(); + $(".task-board-collapsed").show(); + + $(".filter-expand").show(); + $(".task-board-expanded").hide(); + } + + function stack_expand() + { + $(".filter-collapse").show(); + $(".task-board-collapsed").hide(); + + $(".filter-expand").hide(); + $(".task-board-expanded").show(); + } + + function stack_toggle() + { + var state = Kanboard.GetStorageItem(stack_key()) || "expanded"; + + if (state === "expanded") { + stack_collapse(); + Kanboard.SetStorageItem(stack_key(), "collapsed"); + } + else { + stack_expand(); + Kanboard.SetStorageItem(stack_key(), "expanded"); + } + } + + function stack_show() + { + var state = Kanboard.GetStorageItem(stack_key()) || "expanded"; + + if (state === "expanded") { + stack_expand(); + } + else { + stack_collapse(); + } + } + + // Setup the board + function board_load_events() + { + // Drag and drop + $(".column").sortable({ + delay: 300, + distance: 5, + connectWith: ".column", + placeholder: "draggable-placeholder", + stop: function(event, ui) { + board_save( + ui.item.attr('data-task-id'), + ui.item.parent().attr("data-column-id"), + ui.item.index() + 1, + ui.item.parent().attr('data-swimlane-id') + ); + } + }); + + // Task popover + $("#board").on("click", ".task-board-popover", on_popover); + + // Redirect to the task details page + $("#board").on("click", ".task-board", function() { + window.location = $(this).data("task-url"); + }); + + // Tooltips for tasks + $(".task-board-tooltip").tooltip({ + track: false, + position: { + my: 'left-20 top', + at: 'center bottom+9', + using: function(position, feedback) { + + $(this).css(position); + + var arrow_pos = feedback.target.left + feedback.target.width / 2 - feedback.element.left - 20; + + $("<div>") + .addClass("tooltip-arrow") + .addClass(feedback.vertical) + .addClass(arrow_pos == 0 ? "align-left" : "align-right") + .appendTo(this); + } + }, + content: function(e) { + var href = $(this).attr('data-href'); + + if (! href) { + return; + } + + var _this = this; + $.get(href, function setTooltipContent(data) { + + $('.ui-tooltip-content:visible').html(data); + + var tooltip = $('.ui-tooltip:visible'); + + // Clear previous position, it interferes with the updated position computation + tooltip.css({ top: '', left: '' }); + + // Remove arrow, it will be added when repositionning + tooltip.children('.tooltip-arrow').remove(); + + // Reposition the tooltip + var position = $(_this).tooltip("option", "position"); + position.of = $(_this); + tooltip.position(position); + + // Toggle subtasks status + $('#tooltip-subtasks a').not(".popover").click(function(e) { + + e.preventDefault(); + e.stopPropagation(); + + if ($(this).hasClass("popover-subtask-restriction")) { + Kanboard.OpenPopover($(this).attr('href')); + $(_this).tooltip('close'); + } + else { + $.get($(this).attr('href'), setTooltipContent); + } + }); + }); + + return '<i class="fa fa-refresh fa-spin fa-2x"></i>'; + } + }).on("mouseenter", function() { + + var _this = this; + $(this).tooltip("open"); + + $(".ui-tooltip").on("mouseleave", function () { + $(_this).tooltip('close'); + }); + + }).on("mouseleave focusout", function (e) { + + e.stopImmediatePropagation(); + var _this = this; + + setTimeout(function () { + if (! $(".ui-tooltip:hover").length) { + $(_this).tooltip("close"); + } + }, 100); + }); + + // Automatic refresh + var interval = parseInt($("#board").attr("data-check-interval")); + + if (interval > 0) { + checkInterval = window.setInterval(board_check, interval * 1000); + } + } + + // Stop events + function board_unload_events() + { + clearInterval(checkInterval); + } + + // Save and refresh the board + function board_save(taskId, columnId, position, swimlaneId) + { + board_unload_events(); + + $.ajax({ + cache: false, + url: $("#board").attr("data-save-url"), + contentType: "application/json", + type: "POST", + processData: false, + data: JSON.stringify({ + "task_id": taskId, + "column_id": columnId, + "swimlane_id": swimlaneId, + "position": position + }), + success: function(data) { + $("#board-container").remove(); + $("#main").append(data); + Kanboard.InitAfterAjax(); + board_load_events(); + filter_apply(); + stack_show(); + compactview_reload(); + } + }); + } + + // Check if the board have been changed by someone else + function board_check() + { + if (Kanboard.IsVisible()) { + $.ajax({ + cache: false, + url: $("#board").attr("data-check-url"), + statusCode: { + 200: function(data) { + $("#board-container").remove(); + $("#main").append(data); + Kanboard.InitAfterAjax(); + board_unload_events(); + board_load_events(); + filter_apply(); + stack_show(); + compactview_reload(); + } + } + }); + } + } + + // Apply user or date filter (change tasks opacity) + function filter_apply() + { + var selectedUserId = $("#form-user_id").val(); + var selectedCategoryId = $("#form-category_id").val(); + var filterDueDate = $("#more-filters option[value=filter-due-date]").is(":selected") + var filterRecent = $("#more-filters option[value=filter-recent]").is(":selected") + var projectId = $('#board').data('project-id'); + + $("[data-task-id]").each(function(index, item) { + + var ownerId = item.getAttribute("data-owner-id"); + var dueDate = item.getAttribute("data-due-date"); + var categoryId = item.getAttribute("data-category-id"); + var recent = $(item).hasClass("task-board-recent"); + + if (ownerId != selectedUserId && selectedUserId != -1) { + item.style.display = "none"; + } + else { + item.style.display = "block"; + } + + if (filterDueDate && (dueDate == "" || dueDate == "0")) { + item.style.display = "none"; + } + + if (categoryId != selectedCategoryId && selectedCategoryId != -1) { + item.style.display = "none"; + } + + if (filterRecent && ! recent) { + item.style.display = "none"; + } + }); + + // Save filter settings + Kanboard.SetStorageItem("board_filter_" + projectId + "_form-user_id", selectedUserId); + Kanboard.SetStorageItem("board_filter_" + projectId + "_form-category_id", selectedCategoryId); + Kanboard.SetStorageItem("board_filter_" + projectId + "_filter-due-date", ~~(filterDueDate)); + Kanboard.SetStorageItem("board_filter_" + projectId + "_filter-recent", ~~(filterRecent)); + } + + // Load filter events + function filter_load_events() + { + var projectId = $('#board').data('project-id'); + + $("#form-user_id").chosen({ + width: "180px", + no_results_text: $("#form-user_id").data("notfound") + }); + + $("#form-category_id").chosen({ + width: "200px", + no_results_text: $("#form-category_id").data("notfound") + }); + + $("#more-filters").chosen({ + width: "30%", + no_results_text: $("#more-filters").data("notfound") + }); + + $(".apply-filters").change(function(e) { + filter_apply(); + }); + + // Get and set filters from localStorage + $("#form-user_id").val(Kanboard.GetStorageItem("board_filter_" + projectId + "_form-user_id") || -1); + $("#form-user_id").trigger("chosen:updated"); + + $("#form-category_id").val(Kanboard.GetStorageItem("board_filter_" + projectId + "_form-category_id") || -1); + $("#form-category_id").trigger("chosen:updated"); + + if (+Kanboard.GetStorageItem("board_filter_" + projectId + "_filter-due-date")) { + $("#more-filters option[value=filter-due-date]").attr("selected", true); + } + + if (+Kanboard.GetStorageItem("board_filter_" + projectId + "_filter-recent")) { + $("#more-filters option[value=filter-recent]").attr("selected", true); + } + + $("#more-filters").trigger("chosen:updated"); + + filter_apply(); + } + + function compactview_load_events() + { + jQuery(document).on('click', ".filter-toggle-scrolling", function(e) { + e.preventDefault(); + compactview_toggle(); + }); + + compactview_reload(); + } + + function compactview_toggle() + { + var scrolling = Kanboard.GetStorageItem("horizontal_scroll") || 1; + Kanboard.SetStorageItem("horizontal_scroll", scrolling == 0 ? 1 : 0); + compactview_reload(); + } + + function compactview_reload() + { + if (Kanboard.GetStorageItem("horizontal_scroll") == 0) { + + $(".filter-wide").show(); + $(".filter-compact").hide(); + + $("#board-container").addClass("board-container-compact"); + $("#board th").addClass("board-column-compact"); + } + else { + + $(".filter-wide").hide(); + $(".filter-compact").show(); + + $("#board-container").removeClass("board-container-compact"); + $("#board th").removeClass("board-column-compact"); + } + } + + jQuery(document).ready(function() { + + if (Kanboard.Exists("board")) { + board_load_events(); + filter_load_events(); + stack_load_events(); + compactview_load_events(); + keyboard_shortcuts(); + } + }); + +})(); diff --git a/assets/js/src/budget.js b/assets/js/src/budget.js new file mode 100644 index 00000000..ee39c68c --- /dev/null +++ b/assets/js/src/budget.js @@ -0,0 +1,38 @@ +Kanboard.Budget = (function() { + + jQuery(document).ready(function() { + + if (Kanboard.Exists("budget-chart")) { + + var labels =$("#chart").data("labels"); + var serie = $("#chart").data("serie"); + var types = ["in", "out", "left"]; + var data = []; + + for (var i = 0; i < serie.length; i++) { + + for (var j = 0; j < types.length; j++) { + var row = {}; + row[labels["date"]] = serie[i]["date"]; + row[labels["value"]] = serie[i][types[j]]; + row[labels["type"]] = labels[types[j]]; + + data.push(row); + } + } + + var svg = dimple.newSvg("#chart", 750, 600); + var myChart = new dimple.chart(svg, data); + + var x = myChart.addCategoryAxis("x", [labels["date"], labels["type"]]); + x.addOrderRule(labels["date"]); + + myChart.addMeasureAxis("y", labels["value"]); + + myChart.addSeries(labels["type"], dimple.plot.bar); + myChart.addLegend(65, 10, 510, 20, "right"); + myChart.draw(); + } + }); + +})();
\ No newline at end of file diff --git a/assets/js/src/calendar.js b/assets/js/src/calendar.js new file mode 100644 index 00000000..9ee5100d --- /dev/null +++ b/assets/js/src/calendar.js @@ -0,0 +1,152 @@ +Kanboard.Calendar = (function() { + + var filter_storage_key = ""; + + // Save the new due date for a moved task + function move_calendar_event(calendar_event) + { + var url = $("#calendar").data("save-url") || $("#user-calendar").data("save-url"); + + $.ajax({ + cache: false, + url: url, + contentType: "application/json", + type: "POST", + processData: false, + data: JSON.stringify({ + "task_id": calendar_event.id, + "date_due": calendar_event.start.format() + }) + }); + } + + // Show the user calendar + function show_user_calendar() + { + var calendar = $("#user-calendar"); + + calendar.fullCalendar({ + lang: $("body").data("js-lang"), + editable: true, + eventLimit: true, + height: Kanboard.Exists("dashboard-calendar") ? 500 : "auto", + defaultView: "agendaWeek", + header: { + left: 'prev,next today', + center: 'title', + right: 'month,agendaWeek,agendaDay' + }, + viewRender: refresh_user_calendar, + eventDrop: move_calendar_event + }); + } + + // Refresh the calendar events + function refresh_user_calendar() + { + var calendar = $("#user-calendar"); + var url = calendar.data("check-url"); + var params = { + "start": calendar.fullCalendar('getView').start.format(), + "end": calendar.fullCalendar('getView').end.format(), + "user_id": calendar.data("user-id") + }; + + for (var key in params) { + url += "&" + key + "=" + params[key]; + } + + $.getJSON(url, function(events) { + calendar.fullCalendar('removeEvents'); + calendar.fullCalendar('addEventSource', events); + calendar.fullCalendar('rerenderEvents'); + }); + } + + // Show the project calendar + function show_project_calendar() + { + var calendar = $("#calendar"); + + calendar.fullCalendar({ + lang: $("body").data("js-lang"), + editable: true, + eventLimit: true, + defaultView: "month", + header: { + left: 'prev,next today', + center: 'title', + right: 'month,agendaWeek,agendaDay' + }, + viewRender: load_project_filters, + eventDrop: move_calendar_event + }); + } + + // Refresh the calendar events + function refresh_project_calendar(filters) + { + var calendar = $("#calendar"); + var url = calendar.data("check-url"); + var params = { + "start": calendar.fullCalendar('getView').start.format(), + "end": calendar.fullCalendar('getView').end.format() + }; + + jQuery.extend(params, filters); + + for (var key in params) { + url += "&" + key + "=" + params[key]; + } + + $.getJSON(url, function(events) { + calendar.fullCalendar('removeEvents'); + calendar.fullCalendar('addEventSource', events); + calendar.fullCalendar('rerenderEvents'); + }); + } + + // Restore saved filters + function load_project_filters() + { + var filters = Kanboard.GetStorageItem(filter_storage_key); + + if (filters !== "") { + filters = JSON.parse(filters); + + for (var filter in filters) { + $("select[name=" + filter + "]").val(filters[filter]); + } + } + + refresh_project_calendar(filters || {}); + + $('.calendar-filter').change(apply_project_filters); + } + + // Apply filters on change + function apply_project_filters() + { + var filters = {}; + + $('.calendar-filter').each(function() { + filters[$(this).attr("name")] = $(this).val(); + }); + + Kanboard.SetStorageItem(filter_storage_key, JSON.stringify(filters)); + refresh_project_calendar(filters); + } + + jQuery(document).ready(function() { + + if (Kanboard.Exists("calendar")) { + filter_storage_key = "calendar_filters_" + $("#calendar").data("project-id"); + show_project_calendar(); + load_project_filters(); + } + else if (Kanboard.Exists("user-calendar")) { + show_user_calendar(); + } + }); + +})(); diff --git a/assets/js/src/dashboard.js b/assets/js/src/dashboard.js new file mode 100644 index 00000000..d0867e54 --- /dev/null +++ b/assets/js/src/dashboard.js @@ -0,0 +1,52 @@ +Kanboard.Dashboard = (function() { + + jQuery(document).ready(function() { + + var state = Kanboard.GetStorageItem("dashboard_view"); + + if (state) { + + var sections = JSON.parse(state); + + for (var section in sections) { + $("#dashboard-" + section).toggle(sections[section]); + } + + hideColumns(); + } + }); + + jQuery(document).on('click', ".dashboard-toggle", function(e) { + e.preventDefault(); + + $("#dashboard-" + $(this).data("toggle")).toggle(); + hideColumns(); + + var sections = ["projects", "tasks", "subtasks", "activities", "calendar"]; + var state = {}; + + for (var i = 0; i < sections.length; i++) { + state[sections[i]] = $("#dashboard-" + sections[i]).is(":visible"); + } + + Kanboard.SetStorageItem("dashboard_view", JSON.stringify(state)); + }); + + function hideColumns() + { + if ($(".dashboard-right-column > div:visible").size() > 0) { + $(".dashboard-left-column").removeClass("dashboard-single-column"); + } + else { + $(".dashboard-left-column").addClass("dashboard-single-column"); + } + + if ($(".dashboard-left-column > div:visible").size() > 0) { + $(".dashboard-right-column").removeClass("dashboard-single-column"); + } + else { + $(".dashboard-right-column").addClass("dashboard-single-column"); + } + } + +})();
\ No newline at end of file diff --git a/assets/js/src/screenshot.js b/assets/js/src/screenshot.js new file mode 100644 index 00000000..fef37356 --- /dev/null +++ b/assets/js/src/screenshot.js @@ -0,0 +1,102 @@ +Kanboard.Screenshot = (function() { + + var pasteCatcher = null; + + // Setup event listener and workarounds + function init() + { + if (! window.Clipboard) { + + // Create a contenteditable element + pasteCatcher = document.createElement("div"); + pasteCatcher.setAttribute("contenteditable", ""); + pasteCatcher.style.opacity = 0; + document.body.appendChild(pasteCatcher); + + // Make sure it is always in focus + pasteCatcher.focus(); + document.addEventListener("click", function() { pasteCatcher.focus(); }); + } + + window.addEventListener("paste", pasteHandler); + } + + // Paste event callback + function pasteHandler(e) + { + // Firefox doesn't have the property e.clipboardData.items (only Chrome) + if (e.clipboardData && e.clipboardData.items) { + + var items = e.clipboardData.items; + + if (items) { + + for (var i = 0; i < items.length; i++) { + + // Find an image in pasted elements + if (items[i].type.indexOf("image") !== -1) { + + var blob = items[i].getAsFile(); + + // Get the image as base64 data + var reader = new FileReader(); + reader.onload = function(event) { + createImage(event.target.result); + }; + + reader.readAsDataURL(blob); + } + } + } + } + else { + + // Handle Firefox + setTimeout(checkInput, 100); + } + } + + // Parse the input in the paste catcher element + function checkInput() + { + var child = pasteCatcher.childNodes[0]; + pasteCatcher.innerHTML = ""; + + if (child) { + // If the user pastes an image, the src attribute + // will represent the image as a base64 encoded string. + if (child.tagName === "IMG") { + createImage(child.src); + } + } + } + + // Creates a new image from a given source + function createImage(blob) + { + var pastedImage = new Image(); + pastedImage.src = blob; + + // Send the image content to the form variable + pastedImage.onload = function() { + var sourceSplit = blob.split("base64,"); + var sourceString = sourceSplit[1]; + $("input[name=screenshot]").val(sourceString); + }; + + document.getElementById("screenshot-inner").style.display = "none"; + document.getElementById("screenshot-zone").className = "screenshot-pasted"; + document.getElementById("screenshot-zone").appendChild(pastedImage); + } + + jQuery(document).ready(function() { + + if (Kanboard.Exists("screenshot-zone")) { + init(); + } + }); + + return { + Init: init + }; +})();
\ No newline at end of file diff --git a/assets/js/src/swimlane.js b/assets/js/src/swimlane.js new file mode 100644 index 00000000..77f45907 --- /dev/null +++ b/assets/js/src/swimlane.js @@ -0,0 +1,90 @@ +Kanboard.Swimlane = (function() {
+
+ // Expand a Swimlane via display attributes
+ function expand(swimlaneId)
+ {
+ $('.swimlane-row-' + swimlaneId).css('display', 'table-row');
+ $('.show-icon-swimlane-' + swimlaneId).css('display', 'none');
+ $('.hide-icon-swimlane-' + swimlaneId).css('display', 'inline');
+ }
+
+ // Collapse a Swimlane via display attributes
+ function collapse(swimlaneId)
+ {
+ $('.swimlane-row-' + swimlaneId).css('display', 'none');
+ $('.show-icon-swimlane-' + swimlaneId).css('display', 'inline');
+ $('.hide-icon-swimlane-' + swimlaneId).css('display', 'none');
+ }
+
+ // Add swimlane Id to the hidden list and stores the list to localStorage
+ function hide(id)
+ {
+ var storageKey = "hidden_swimlanes_" + $("#board").data("project-id");
+ var hiddenSwimlaneIds = JSON.parse(Kanboard.GetStorageItem(storageKey)) || [];
+
+ hiddenSwimlaneIds.push(id);
+
+ Kanboard.SetStorageItem(storageKey, JSON.stringify(hiddenSwimlaneIds));
+ }
+
+ // Remove swimlane Id from the hidden list and stores the list to
+ // localStorage
+ function unhide(id)
+ {
+ var storageKey = "hidden_swimlanes_" + $("#board").data("project-id");
+ var hiddenSwimlaneIds = JSON.parse(Kanboard.GetStorageItem(storageKey)) || [];
+ var index = hiddenSwimlaneIds.indexOf(id);
+
+ if (index > -1) {
+ hiddenSwimlaneIds.splice(index, 1);
+ }
+
+ Kanboard.SetStorageItem(storageKey, JSON.stringify(hiddenSwimlaneIds));
+ }
+
+ // Check if swimlane Id is hidden. Anything > -1 means hidden.
+ function isHidden(id)
+ {
+ return getAllHidden().indexOf(id) > -1;
+ }
+
+ // Gets all swimlane Ids that are hidden
+ function getAllHidden()
+ {
+ var storageKey = "hidden_swimlanes_" + $("#board").data("project-id");
+ return JSON.parse(Kanboard.GetStorageItem(storageKey)) || [];
+ }
+
+ // Reload the swimlane states (shown/hidden) after an ajax call
+ jQuery(document).ajaxComplete(function() {
+
+ getAllHidden().map(function(swimlaneId) {
+ collapse(swimlaneId);
+ });
+ });
+
+ // Reload the swimlane states (shown/hidden) after page refresh
+ jQuery(document).ready(function() {
+
+ getAllHidden().map(function(swimlaneId) {
+ collapse(swimlaneId);
+ });
+ });
+
+ // Clicking on Show/Hide icon fires this.
+ jQuery(document).on('click', ".board-swimlane-toggle", function(e) {
+ e.preventDefault();
+
+ var swimlaneId = $(this).data('swimlane-id');
+
+ if (isHidden(swimlaneId)) {
+ unhide(swimlaneId);
+ expand(swimlaneId);
+ }
+ else {
+ hide(swimlaneId);
+ collapse(swimlaneId);
+ }
+ });
+
+})();
|