Javascript refactoring
This commit is contained in:
@@ -167,10 +167,12 @@ class ProjectAnalytic extends Base
|
|||||||
$sums[$task['column_id']] += ($task['date_completed'] ?: time()) - $task['date_moved'];
|
$sums[$task['column_id']] += ($task['date_completed'] ?: time()) - $task['date_moved'];
|
||||||
|
|
||||||
foreach ($sums as $column_id => $time_spent) {
|
foreach ($sums as $column_id => $time_spent) {
|
||||||
|
if (isset($stats[$column_id])) {
|
||||||
$stats[$column_id]['count']++;
|
$stats[$column_id]['count']++;
|
||||||
$stats[$column_id]['time_spent'] += $time_spent;
|
$stats[$column_id]['time_spent'] += $time_spent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate average for each column
|
// Calculate average for each column
|
||||||
foreach ($columns as $column_id => $column_title) {
|
foreach ($columns as $column_id => $column_title) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<th class="board-column-header">
|
<th class="board-column-header">
|
||||||
<?php if (! $not_editable): ?>
|
<?php if (! $not_editable): ?>
|
||||||
<div class="board-add-icon">
|
<div class="board-add-icon">
|
||||||
<?= $this->url->link('+', 'taskcreation', 'create', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'task-board-popover', t('Add a new task')) ?>
|
<?= $this->url->link('+', 'taskcreation', 'create', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover', t('Add a new task')) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
'changeCategory',
|
'changeCategory',
|
||||||
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
||||||
false,
|
false,
|
||||||
'task-board-popover' . (! empty($task['category_description']) ? ' tooltip' : ''),
|
'popover' . (! empty($task['category_description']) ? ' tooltip' : ''),
|
||||||
! empty($task['category_description']) ? $this->text->markdown($task['category_description']) : t('Change category')
|
! empty($task['category_description']) ? $this->text->markdown($task['category_description']) : t('Change category')
|
||||||
) ?>
|
) ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
@@ -27,31 +27,31 @@
|
|||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if ($task['recurrence_status'] == \Model\Task::RECURRING_STATUS_PENDING): ?>
|
<?php if ($task['recurrence_status'] == \Model\Task::RECURRING_STATUS_PENDING): ?>
|
||||||
<span title="<?= t('Recurrence') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90"></i></span>
|
<span title="<?= t('Recurrence') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90"></i></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if ($task['recurrence_status'] == \Model\Task::RECURRING_STATUS_PROCESSED): ?>
|
<?php if ($task['recurrence_status'] == \Model\Task::RECURRING_STATUS_PROCESSED): ?>
|
||||||
<span title="<?= t('Recurrence') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90 fa-inverse"></i></span>
|
<span title="<?= t('Recurrence') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90 fa-inverse"></i></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (! empty($task['nb_links'])): ?>
|
<?php if (! empty($task['nb_links'])): ?>
|
||||||
<span title="<?= t('Links') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork"></i> <?= $task['nb_links'] ?></span>
|
<span title="<?= t('Links') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork"></i> <?= $task['nb_links'] ?></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (! empty($task['nb_subtasks'])): ?>
|
<?php if (! empty($task['nb_subtasks'])): ?>
|
||||||
<span title="<?= t('Sub-Tasks') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'subtasks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-bars"></i> <?= round($task['nb_completed_subtasks']/$task['nb_subtasks']*100, 0).'%' ?></span>
|
<span title="<?= t('Sub-Tasks') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'subtasks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-bars"></i> <?= round($task['nb_completed_subtasks']/$task['nb_subtasks']*100, 0).'%' ?></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (! empty($task['nb_files'])): ?>
|
<?php if (! empty($task['nb_files'])): ?>
|
||||||
<span title="<?= t('Attachments') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'attachments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-paperclip"></i> <?= $task['nb_files'] ?></span>
|
<span title="<?= t('Attachments') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'attachments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-paperclip"></i> <?= $task['nb_files'] ?></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (! empty($task['nb_comments'])): ?>
|
<?php if (! empty($task['nb_comments'])): ?>
|
||||||
<span title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'comments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-comment-o"></i> <?= $task['nb_comments'] ?></span>
|
<span title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>" class="tooltip" data-href="<?= $this->url->href('board', 'comments', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-comment-o"></i> <?= $task['nb_comments'] ?></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (! empty($task['description'])): ?>
|
<?php if (! empty($task['description'])): ?>
|
||||||
<span title="<?= t('Description') ?>" class="task-board-tooltip" data-href="<?= $this->url->href('board', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
|
<span title="<?= t('Description') ?>" class="tooltip" data-href="<?= $this->url->href('board', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
|
||||||
<i class="fa fa-file-text-o"></i>
|
<i class="fa fa-file-text-o"></i>
|
||||||
</span>
|
</span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
<span>
|
<span>
|
||||||
<a href="#" class="dropdown-menu"><?= '#'.$task['id'] ?></a>
|
<a href="#" class="dropdown-menu"><?= '#'.$task['id'] ?></a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><i class="fa fa-user"></i> <?= $this->url->link(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-user"></i> <?= $this->url->link(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-tag"></i> <?= $this->url->link(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-tag"></i> <?= $this->url->link(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-align-left"></i> <?= $this->url->link(t('Change description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-align-left"></i> <?= $this->url->link(t('Change description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-pencil-square-o"></i> <?= $this->url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-pencil-square-o"></i> <?= $this->url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-comment-o"></i> <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-comment-o"></i> <?= $this->url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-code-fork"></i> <?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-code-fork"></i> <?= $this->url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-camera"></i> <?= $this->url->link(t('Add a screenshot'), 'board', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-camera"></i> <?= $this->url->link(t('Add a screenshot'), 'board', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
|
||||||
<li><i class="fa fa-close"></i> <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'task-board-popover') ?></li>
|
<li><i class="fa fa-close"></i> <?= $this->url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?></li>
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
'changeAssignee',
|
'changeAssignee',
|
||||||
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
|
||||||
false,
|
false,
|
||||||
'task-board-popover',
|
'popover',
|
||||||
t('Change assignee')
|
t('Change assignee')
|
||||||
) ?>
|
) ?>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
196
assets/js/src/App.js
Normal file
196
assets/js/src/App.js
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
function App() {
|
||||||
|
this.popover = new Popover(this);
|
||||||
|
this.markdown = new Markdown();
|
||||||
|
this.sidebar = new Sidebar();
|
||||||
|
this.search = new Search();
|
||||||
|
this.tooltip = new Tooltip(this);
|
||||||
|
this.swimlane = new Swimlane();
|
||||||
|
this.keyboardShortcuts();
|
||||||
|
this.boardSelector();
|
||||||
|
this.listen();
|
||||||
|
this.poll();
|
||||||
|
|
||||||
|
// Alert box fadeout
|
||||||
|
$(".alert-fade-out").delay(4000).fadeOut(800, function() {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reload page when a destination project is changed
|
||||||
|
var reloading_project = false;
|
||||||
|
$("select.task-reload-project-destination").change(function() {
|
||||||
|
if (! reloading_project) {
|
||||||
|
$(".loading-icon").show();
|
||||||
|
reloading_project = true;
|
||||||
|
window.location = $(this).data("redirect").replace(/PROJECT_ID/g, $(this).val());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
App.prototype.listen = function() {
|
||||||
|
$(document).off();
|
||||||
|
|
||||||
|
this.popover.listen();
|
||||||
|
this.markdown.listen();
|
||||||
|
this.sidebar.listen();
|
||||||
|
this.tooltip.listen();
|
||||||
|
this.search.listen();
|
||||||
|
this.search.focus();
|
||||||
|
this.taskAutoComplete();
|
||||||
|
this.datePicker();
|
||||||
|
this.focus();
|
||||||
|
|
||||||
|
// Dropdown
|
||||||
|
$(".dropit-submenu").hide();
|
||||||
|
$('.dropdown').not(".dropit").dropit({ triggerParentEl : "span" });
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.focus = function() {
|
||||||
|
|
||||||
|
// Autofocus fields (html5 autofocus works only with page onload)
|
||||||
|
$("[autofocus]").each(function(index, element) {
|
||||||
|
$(this).focus();
|
||||||
|
})
|
||||||
|
|
||||||
|
// Auto-select input fields
|
||||||
|
$(document).on('focus', '.auto-select', function() {
|
||||||
|
$(this).select();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workaround for chrome
|
||||||
|
$(document).on('mouseup', '.auto-select', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.poll = function() {
|
||||||
|
window.setInterval(this.checkSession, 60000);
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.keyboardShortcuts = function() {
|
||||||
|
// Submit form
|
||||||
|
Mousetrap.bindGlobal("mod+enter", function() {
|
||||||
|
$("form").submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Open board selector
|
||||||
|
Mousetrap.bind("b", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#board-selector').trigger('chosen:open');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.checkSession = function() {
|
||||||
|
if (! $(".form-login").length) {
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
url: $("body").data("status-url"),
|
||||||
|
statusCode: {
|
||||||
|
401: function() {
|
||||||
|
window.location = $("body").data("login-url");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.datePicker = function() {
|
||||||
|
// Datepicker translation
|
||||||
|
$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);
|
||||||
|
|
||||||
|
// Datepicker
|
||||||
|
$(".form-date").datepicker({
|
||||||
|
showOtherMonths: true,
|
||||||
|
selectOtherMonths: true,
|
||||||
|
dateFormat: 'yy-mm-dd',
|
||||||
|
constrainInput: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Datetime picker
|
||||||
|
$(".form-datetime").datetimepicker({
|
||||||
|
controlType: 'select',
|
||||||
|
oneLine: true,
|
||||||
|
dateFormat: 'yy-mm-dd',
|
||||||
|
// timeFormat: 'h:mm tt',
|
||||||
|
constrainInput: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.taskAutoComplete = function() {
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.boardSelector = function() {
|
||||||
|
$(".chosen-select").chosen({
|
||||||
|
width: "200px",
|
||||||
|
no_results_text: $(".chosen-select").data("notfound"),
|
||||||
|
disable_search_threshold: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#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());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.showLoadingIcon = function() {
|
||||||
|
$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>');
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.hideLoadingIcon = function() {
|
||||||
|
$("#app-loading-icon").remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.formatDuration = function(d) {
|
||||||
|
if (d >= 86400) {
|
||||||
|
return Math.round(d/86400) + "d";
|
||||||
|
}
|
||||||
|
else if (d >= 3600) {
|
||||||
|
return Math.round(d/3600) + "h";
|
||||||
|
}
|
||||||
|
else if (d >= 60) {
|
||||||
|
return Math.round(d/60) + "m";
|
||||||
|
}
|
||||||
|
|
||||||
|
return d + "s";
|
||||||
|
};
|
||||||
39
assets/js/src/AvgTimeColumnChart.js
Normal file
39
assets/js/src/AvgTimeColumnChart.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
function AvgTimeColumnChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
AvgTimeColumnChart.prototype.execute = function(app) {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var plots = [$("#chart").data("label")];
|
||||||
|
var categories = [];
|
||||||
|
|
||||||
|
for (var column_id in metrics) {
|
||||||
|
plots.push(metrics[column_id].average);
|
||||||
|
categories.push(metrics[column_id].title);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: [plots],
|
||||||
|
type: 'bar'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
width: {
|
||||||
|
ratio: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: {
|
||||||
|
format: app.formatDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
55
assets/js/src/BudgetChart.js
Normal file
55
assets/js/src/BudgetChart.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
function BudgetChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
BudgetChart.prototype.execute = function() {
|
||||||
|
var categories = [];
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var labels = $("#chart").data("labels");
|
||||||
|
var inputFormat = d3.time.format("%Y-%m-%d");
|
||||||
|
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
||||||
|
|
||||||
|
var columns = [
|
||||||
|
[labels["in"]],
|
||||||
|
[labels["left"]],
|
||||||
|
[labels["out"]]
|
||||||
|
];
|
||||||
|
|
||||||
|
var colors = {};
|
||||||
|
colors[labels["in"]] = '#5858FA';
|
||||||
|
colors[labels["left"]] = '#04B404';
|
||||||
|
colors[labels["out"]] = '#DF3A01';
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
categories.push(outputFormat(inputFormat.parse(metrics[i]["date"])));
|
||||||
|
columns[0].push(metrics[i]["in"]);
|
||||||
|
columns[1].push(metrics[i]["left"]);
|
||||||
|
columns[2].push(metrics[i]["out"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
colors: colors,
|
||||||
|
type : 'bar'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
width: {
|
||||||
|
ratio: 0.25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
x: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
48
assets/js/src/BurndownChart.js
Normal file
48
assets/js/src/BurndownChart.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
function BurndownChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
BurndownChart.prototype.execute = function() {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var columns = [[$("#chart").data("label-total")]];
|
||||||
|
var categories = [];
|
||||||
|
var inputFormat = d3.time.format("%Y-%m-%d");
|
||||||
|
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
|
||||||
|
for (var j = 0; j < metrics[i].length; j++) {
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
columns.push([metrics[i][j]]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columns[j + 1].push(metrics[i][j]);
|
||||||
|
|
||||||
|
if (j > 0) {
|
||||||
|
|
||||||
|
if (columns[0][i] == undefined) {
|
||||||
|
columns[0].push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
columns[0][i] += metrics[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == 0) {
|
||||||
|
categories.push(outputFormat(inputFormat.parse(metrics[i][j])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: columns
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
48
assets/js/src/CumulativeFlowDiagram.js
Normal file
48
assets/js/src/CumulativeFlowDiagram.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
function CumulativeFlowDiagram() {
|
||||||
|
}
|
||||||
|
|
||||||
|
CumulativeFlowDiagram.prototype.execute = function() {
|
||||||
|
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var columns = [];
|
||||||
|
var groups = [];
|
||||||
|
var categories = [];
|
||||||
|
var inputFormat = d3.time.format("%Y-%m-%d");
|
||||||
|
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
|
||||||
|
for (var j = 0; j < metrics[i].length; j++) {
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
columns.push([metrics[i][j]]);
|
||||||
|
|
||||||
|
if (j > 0) {
|
||||||
|
groups.push(metrics[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
columns[j].push(metrics[i][j]);
|
||||||
|
|
||||||
|
if (j == 0) {
|
||||||
|
categories.push(outputFormat(inputFormat.parse(metrics[i][j])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type: 'area-spline',
|
||||||
|
groups: [groups]
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
45
assets/js/src/LeadCycleTimeChart.js
Normal file
45
assets/js/src/LeadCycleTimeChart.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
function LeadCycleTimeChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
LeadCycleTimeChart.prototype.execute = function(app) {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var cycle = [$("#chart").data("label-cycle")];
|
||||||
|
var lead = [$("#chart").data("label-lead")];
|
||||||
|
var categories = [];
|
||||||
|
|
||||||
|
var types = {};
|
||||||
|
types[$("#chart").data("label-cycle")] = 'area';
|
||||||
|
types[$("#chart").data("label-lead")] = 'area-spline';
|
||||||
|
|
||||||
|
var colors = {};
|
||||||
|
colors[$("#chart").data("label-lead")] = '#afb42b';
|
||||||
|
colors[$("#chart").data("label-cycle")] = '#4e342e';
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
cycle.push(parseInt(metrics[i].avg_cycle_time));
|
||||||
|
lead.push(parseInt(metrics[i].avg_lead_time));
|
||||||
|
categories.push(metrics[i].day);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: [
|
||||||
|
lead,
|
||||||
|
cycle
|
||||||
|
],
|
||||||
|
types: types,
|
||||||
|
colors: colors
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: {
|
||||||
|
format: app.formatDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
50
assets/js/src/Markdown.js
Normal file
50
assets/js/src/Markdown.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
function Markdown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Markdown.prototype.showPreview = 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", // TODO: remoe harcoded url
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Markdown.prototype.showWriter = 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
Markdown.prototype.listen = function() {
|
||||||
|
$(document).on("click", "#markdown-preview", this.showPreview.bind(this));
|
||||||
|
$(document).on("click", "#markdown-write", this.showWriter.bind(this));
|
||||||
|
};
|
||||||
50
assets/js/src/Popover.js
Normal file
50
assets/js/src/Popover.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
function Popover(app) {
|
||||||
|
this.app = app;
|
||||||
|
this.router = new Router();
|
||||||
|
this.router.addRoute('screenshot-zone', Screenshot);
|
||||||
|
Mousetrap.bindGlobal("esc", this.close);
|
||||||
|
}
|
||||||
|
|
||||||
|
Popover.prototype.isOpen = function() {
|
||||||
|
return $('#popover-container').size() > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Popover.prototype.open = function(link) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
$.get(link, function(content) {
|
||||||
|
$("body").append('<div id="popover-container"><div id="popover-content">' + content + '</div></div>');
|
||||||
|
self.router.dispatch();
|
||||||
|
self.app.listen();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Popover.prototype.close = function(e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#popover-container').remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
Popover.prototype.onClick = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var link = e.target.getAttribute("href");
|
||||||
|
|
||||||
|
if (! link) {
|
||||||
|
link = e.target.getAttribute("data-href");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link) {
|
||||||
|
this.open(link);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Popover.prototype.listen = function() {
|
||||||
|
$(document).on("click", ".popover", this.onClick.bind(this));
|
||||||
|
$(document).on("click", ".close-popover", this.close.bind(this));
|
||||||
|
$(document).on("click", "#popover-container", this.close.bind(this));
|
||||||
|
$(document).on("click", "#popover-content", function(e) { e.stopPropagation(); });
|
||||||
|
};
|
||||||
34
assets/js/src/Router.js
Normal file
34
assets/js/src/Router.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
function Router() {
|
||||||
|
this.routes = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Router.prototype.addRoute = function(id, controller) {
|
||||||
|
this.routes[id] = controller;
|
||||||
|
};
|
||||||
|
|
||||||
|
Router.prototype.dispatch = function(app) {
|
||||||
|
for (var id in this.routes) {
|
||||||
|
if (document.getElementById(id)) {
|
||||||
|
var controller = Object.create(this.routes[id].prototype);
|
||||||
|
controller.execute(app);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery(document).ready(function() {
|
||||||
|
var app = new App();
|
||||||
|
var router = new Router();
|
||||||
|
router.addRoute('board', Board);
|
||||||
|
router.addRoute('calendar', Calendar);
|
||||||
|
router.addRoute('screenshot-zone', Screenshot);
|
||||||
|
router.addRoute('analytic-task-repartition', TaskRepartitionChart);
|
||||||
|
router.addRoute('analytic-user-repartition', UserRepartitionChart);
|
||||||
|
router.addRoute('analytic-cfd', CumulativeFlowDiagram);
|
||||||
|
router.addRoute('analytic-burndown', BurndownChart);
|
||||||
|
router.addRoute('budget-chart', BudgetChart);
|
||||||
|
router.addRoute('analytic-avg-time-column', AvgTimeColumnChart);
|
||||||
|
router.addRoute('analytic-task-time-column', TaskTimeColumnChart);
|
||||||
|
router.addRoute('analytic-lead-cycle-time', LeadCycleTimeChart);
|
||||||
|
router.dispatch(app);
|
||||||
|
});
|
||||||
61
assets/js/src/Search.js
Normal file
61
assets/js/src/Search.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
function Search() {
|
||||||
|
this.keyboardShortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
|
Search.prototype.focus = function() {
|
||||||
|
// Place cursor at the end when focusing on the search box
|
||||||
|
$(document).on("focus", "#form-search", function() {
|
||||||
|
if ($("#form-search")[0].setSelectionRange) {
|
||||||
|
$('#form-search')[0].setSelectionRange($('#form-search').val().length, $('#form-search').val().length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Search.prototype.listen = function() {
|
||||||
|
// Filter helper for search
|
||||||
|
$(document).on("click", ".filter-helper", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$("#form-search").val($(this).data("filter"));
|
||||||
|
$("form.search").submit();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Search.prototype.keyboardShortcuts = function() {
|
||||||
|
// Switch view mode for projects: go to the board
|
||||||
|
Mousetrap.bind("v b", function(e) {
|
||||||
|
var link = $(".view-board");
|
||||||
|
|
||||||
|
if (link.length) {
|
||||||
|
window.location = link.attr('href');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Switch view mode for projects: go to the calendar
|
||||||
|
Mousetrap.bind("v c", function(e) {
|
||||||
|
var link = $(".view-calendar");
|
||||||
|
|
||||||
|
if (link.length) {
|
||||||
|
window.location = link.attr('href');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Switch view mode for projects: go to the listing
|
||||||
|
Mousetrap.bind("v l", function(e) {
|
||||||
|
var link = $(".view-listing");
|
||||||
|
|
||||||
|
if (link.length) {
|
||||||
|
window.location = link.attr('href');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Focus to the search field
|
||||||
|
Mousetrap.bind("f", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var input = document.getElementById("form-search");
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
25
assets/js/src/Sidebar.js
Normal file
25
assets/js/src/Sidebar.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
function Sidebar() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Sidebar.prototype.expand = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(".sidebar-container").removeClass("sidebar-collapsed");
|
||||||
|
$(".sidebar-collapse").show();
|
||||||
|
$(".sidebar h2").show();
|
||||||
|
$(".sidebar ul").show();
|
||||||
|
$(".sidebar-expand").hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
Sidebar.prototype.collapse = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(".sidebar-container").addClass("sidebar-collapsed");
|
||||||
|
$(".sidebar-expand").show();
|
||||||
|
$(".sidebar h2").hide();
|
||||||
|
$(".sidebar ul").hide();
|
||||||
|
$(".sidebar-collapse").hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
Sidebar.prototype.listen = function() {
|
||||||
|
$(document).on("click", ".sidebar-collapse", this.collapse);
|
||||||
|
$(document).on("click", ".sidebar-expand", this.expand);
|
||||||
|
};
|
||||||
18
assets/js/src/TaskRepartitionChart.js
Normal file
18
assets/js/src/TaskRepartitionChart.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
function TaskRepartitionChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskRepartitionChart.prototype.execute = function() {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var columns = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
columns.push([metrics[i].column_title, metrics[i].nb_tasks]);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type : 'donut'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
39
assets/js/src/TaskTimeColumnChart.js
Normal file
39
assets/js/src/TaskTimeColumnChart.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
function TaskTimeColumnChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskTimeColumnChart.prototype.execute = function(app) {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var plots = [$("#chart").data("label")];
|
||||||
|
var categories = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
plots.push(metrics[i].time_spent);
|
||||||
|
categories.push(metrics[i].title);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: [plots],
|
||||||
|
type: 'bar'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
width: {
|
||||||
|
ratio: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: {
|
||||||
|
format: app.formatDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
87
assets/js/src/Tooltip.js
Normal file
87
assets/js/src/Tooltip.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
function Tooltip(app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tooltip.prototype.listen = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
$(".tooltip").tooltip({
|
||||||
|
track: false,
|
||||||
|
show: 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 < 1 ? "align-left" : "align-right")
|
||||||
|
.appendTo(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: function() {
|
||||||
|
var _this = this;
|
||||||
|
var href = $(this).attr('data-href');
|
||||||
|
|
||||||
|
if (! href) {
|
||||||
|
return '<div class="markdown">' + $(this).attr("title") + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(href, function setTooltipContent(data) {
|
||||||
|
var tooltip = $('.ui-tooltip:visible');
|
||||||
|
|
||||||
|
$('.ui-tooltip-content:visible').html(data);
|
||||||
|
|
||||||
|
// 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")) {
|
||||||
|
self.app.popover.open($(this).attr('href'));
|
||||||
|
$(_this).tooltip('close');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$.get($(this).attr('href'), setTooltipContent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return '<i class="fa fa-spinner fa-spin"></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);
|
||||||
|
});
|
||||||
|
};
|
||||||
18
assets/js/src/UserRepartitionChart.js
Normal file
18
assets/js/src/UserRepartitionChart.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
function UserRepartitionChart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
UserRepartitionChart.prototype.execute = function() {
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var columns = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
columns.push([metrics[i].user, metrics[i].nb_tasks]);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: columns,
|
||||||
|
type : 'donut'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
(function() {
|
|
||||||
|
|
||||||
// CFD diagram
|
|
||||||
function drawCfd()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var columns = [];
|
|
||||||
var groups = [];
|
|
||||||
var categories = [];
|
|
||||||
var inputFormat = d3.time.format("%Y-%m-%d");
|
|
||||||
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
|
|
||||||
for (var j = 0; j < metrics[i].length; j++) {
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
columns.push([metrics[i][j]]);
|
|
||||||
|
|
||||||
if (j > 0) {
|
|
||||||
groups.push(metrics[i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
columns[j].push(metrics[i][j]);
|
|
||||||
|
|
||||||
if (j == 0) {
|
|
||||||
categories.push(outputFormat(inputFormat.parse(metrics[i][j])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: columns,
|
|
||||||
type: 'area-spline',
|
|
||||||
groups: [groups]
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Burndown chart
|
|
||||||
function drawBurndown()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var columns = [[$("#chart").data("label-total")]];
|
|
||||||
var categories = [];
|
|
||||||
var inputFormat = d3.time.format("%Y-%m-%d");
|
|
||||||
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
|
|
||||||
for (var j = 0; j < metrics[i].length; j++) {
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
columns.push([metrics[i][j]]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
columns[j + 1].push(metrics[i][j]);
|
|
||||||
|
|
||||||
if (j > 0) {
|
|
||||||
|
|
||||||
if (columns[0][i] == undefined) {
|
|
||||||
columns[0].push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
columns[0][i] += metrics[i][j];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j == 0) {
|
|
||||||
categories.push(outputFormat(inputFormat.parse(metrics[i][j])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: columns
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw task repartition chart
|
|
||||||
function drawTaskRepartition()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var columns = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
columns.push([metrics[i].column_title, metrics[i].nb_tasks]);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: columns,
|
|
||||||
type : 'donut'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw user repartition chart
|
|
||||||
function drawUserRepartition()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var columns = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
columns.push([metrics[i].user, metrics[i].nb_tasks]);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: columns,
|
|
||||||
type : 'donut'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw budget chart
|
|
||||||
function drawBudget()
|
|
||||||
{
|
|
||||||
var categories = [];
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var labels = $("#chart").data("labels");
|
|
||||||
var inputFormat = d3.time.format("%Y-%m-%d");
|
|
||||||
var outputFormat = d3.time.format($("#chart").data("date-format"));
|
|
||||||
|
|
||||||
var columns = [
|
|
||||||
[labels["in"]],
|
|
||||||
[labels["left"]],
|
|
||||||
[labels["out"]]
|
|
||||||
];
|
|
||||||
|
|
||||||
var colors = {};
|
|
||||||
colors[labels["in"]] = '#5858FA';
|
|
||||||
colors[labels["left"]] = '#04B404';
|
|
||||||
colors[labels["out"]] = '#DF3A01';
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
categories.push(outputFormat(inputFormat.parse(metrics[i]["date"])));
|
|
||||||
columns[0].push(metrics[i]["in"]);
|
|
||||||
columns[1].push(metrics[i]["left"]);
|
|
||||||
columns[2].push(metrics[i]["out"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: columns,
|
|
||||||
colors: colors,
|
|
||||||
type : 'bar'
|
|
||||||
},
|
|
||||||
bar: {
|
|
||||||
width: {
|
|
||||||
ratio: 0.25
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
x: {
|
|
||||||
show: true
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
show: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw chart for average time spent into each column
|
|
||||||
function drawAvgTimeColumn()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var plots = [$("#chart").data("label")];
|
|
||||||
var categories = [];
|
|
||||||
|
|
||||||
for (var column_id in metrics) {
|
|
||||||
plots.push(metrics[column_id].average);
|
|
||||||
categories.push(metrics[column_id].title);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: [plots],
|
|
||||||
type: 'bar'
|
|
||||||
},
|
|
||||||
bar: {
|
|
||||||
width: {
|
|
||||||
ratio: 0.5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
tick: {
|
|
||||||
format: formatDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw chart for average time spent into each column
|
|
||||||
function drawTaskTimeColumn()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var plots = [$("#chart").data("label")];
|
|
||||||
var categories = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
plots.push(metrics[i].time_spent);
|
|
||||||
categories.push(metrics[i].title);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: [plots],
|
|
||||||
type: 'bar'
|
|
||||||
},
|
|
||||||
bar: {
|
|
||||||
width: {
|
|
||||||
ratio: 0.5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
tick: {
|
|
||||||
format: formatDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw lead and cycle time for the project
|
|
||||||
function drawLeadAndCycleTime()
|
|
||||||
{
|
|
||||||
var metrics = $("#chart").data("metrics");
|
|
||||||
var cycle = [$("#chart").data("label-cycle")];
|
|
||||||
var lead = [$("#chart").data("label-lead")];
|
|
||||||
var categories = [];
|
|
||||||
|
|
||||||
var types = {};
|
|
||||||
types[$("#chart").data("label-cycle")] = 'area';
|
|
||||||
types[$("#chart").data("label-lead")] = 'area-spline';
|
|
||||||
|
|
||||||
var colors = {};
|
|
||||||
colors[$("#chart").data("label-lead")] = '#afb42b';
|
|
||||||
colors[$("#chart").data("label-cycle")] = '#4e342e';
|
|
||||||
|
|
||||||
for (var i = 0; i < metrics.length; i++) {
|
|
||||||
cycle.push(parseInt(metrics[i].avg_cycle_time));
|
|
||||||
lead.push(parseInt(metrics[i].avg_lead_time));
|
|
||||||
categories.push(metrics[i].day);
|
|
||||||
}
|
|
||||||
|
|
||||||
c3.generate({
|
|
||||||
data: {
|
|
||||||
columns: [
|
|
||||||
lead,
|
|
||||||
cycle
|
|
||||||
],
|
|
||||||
types: types,
|
|
||||||
colors: colors
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
type: 'category',
|
|
||||||
categories: categories
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
tick: {
|
|
||||||
format: formatDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDuration(d)
|
|
||||||
{
|
|
||||||
if (d >= 86400) {
|
|
||||||
return Math.round(d/86400) + "d";
|
|
||||||
}
|
|
||||||
else if (d >= 3600) {
|
|
||||||
return Math.round(d/3600) + "h";
|
|
||||||
}
|
|
||||||
else if (d >= 60) {
|
|
||||||
return Math.round(d/60) + "m";
|
|
||||||
}
|
|
||||||
|
|
||||||
return d + "s";
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
|
|
||||||
if (Kanboard.Exists("analytic-task-repartition")) {
|
|
||||||
drawTaskRepartition();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-user-repartition")) {
|
|
||||||
drawUserRepartition();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-cfd")) {
|
|
||||||
drawCfd();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-burndown")) {
|
|
||||||
drawBurndown();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("budget-chart")) {
|
|
||||||
drawBudget();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-avg-time-column")) {
|
|
||||||
drawAvgTimeColumn();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-task-time-column")) {
|
|
||||||
drawTaskTimeColumn();
|
|
||||||
}
|
|
||||||
else if (Kanboard.Exists("analytic-lead-cycle-time")) {
|
|
||||||
drawLeadAndCycleTime();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,394 +0,0 @@
|
|||||||
var Kanboard = (function() {
|
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
Kanboard.Init();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
ShowLoadingIcon: function() {
|
|
||||||
$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>');
|
|
||||||
},
|
|
||||||
|
|
||||||
HideLoadingIcon: function() {
|
|
||||||
$("#app-loading-icon").remove();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
Kanboard.ClosePopover();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#popover-content").click(function(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".close-popover").click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
Kanboard.ClosePopover();
|
|
||||||
});
|
|
||||||
|
|
||||||
Mousetrap.bindGlobal("esc", function() {
|
|
||||||
Kanboard.ClosePopover();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
ClosePopover: function() {
|
|
||||||
$('#popover-container').remove();
|
|
||||||
Kanboard.Screenshot.Destroy();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Submit form
|
|
||||||
Mousetrap.bindGlobal("mod+enter", function() {
|
|
||||||
$("form").submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open board selector
|
|
||||||
Mousetrap.bind("b", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$('#board-selector').trigger('chosen:open');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus to the search box
|
|
||||||
Mousetrap.bind("f", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var input = document.getElementById("form-search");
|
|
||||||
|
|
||||||
if (input) {
|
|
||||||
input.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Switch view mode for projects: go to the board
|
|
||||||
Mousetrap.bind("v b", function(e) {
|
|
||||||
var link = $(".view-board");
|
|
||||||
|
|
||||||
if (link.length) {
|
|
||||||
window.location = link.attr('href');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Switch view mode for projects: go to the calendar
|
|
||||||
Mousetrap.bind("v c", function(e) {
|
|
||||||
var link = $(".view-calendar");
|
|
||||||
|
|
||||||
if (link.length) {
|
|
||||||
window.location = link.attr('href');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Switch view mode for projects: go to the listing
|
|
||||||
Mousetrap.bind("v l", function(e) {
|
|
||||||
var link = $(".view-listing");
|
|
||||||
|
|
||||||
if (link.length) {
|
|
||||||
window.location = link.attr('href');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Place cursor at the end when focusing on the search box
|
|
||||||
$(document).on("focus", "#form-search", function() {
|
|
||||||
if ($("#form-search")[0].setSelectionRange) {
|
|
||||||
$('#form-search')[0].setSelectionRange($('#form-search').val().length, $('#form-search').val().length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Filter helper for search
|
|
||||||
$(document).on("click", ".filter-helper", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$("#form-search").val($(this).data("filter"));
|
|
||||||
$("form.search").submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Collapse sidebar
|
|
||||||
$(document).on("click", ".sidebar-collapse", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$(".sidebar-container").addClass("sidebar-collapsed");
|
|
||||||
$(".sidebar-expand").show();
|
|
||||||
$(".sidebar h2").hide();
|
|
||||||
$(".sidebar ul").hide();
|
|
||||||
$(".sidebar-collapse").hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expand sidebar
|
|
||||||
$(document).on("click", ".sidebar-expand", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$(".sidebar-container").removeClass("sidebar-collapsed");
|
|
||||||
$(".sidebar-collapse").show();
|
|
||||||
$(".sidebar h2").show();
|
|
||||||
$(".sidebar ul").show();
|
|
||||||
$(".sidebar-expand").hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reload page when a destination project is changed
|
|
||||||
var reloading_project = false;
|
|
||||||
$("select.task-reload-project-destination").change(function() {
|
|
||||||
if (! reloading_project) {
|
|
||||||
$(".loading-icon").show();
|
|
||||||
reloading_project = true;
|
|
||||||
window.location = $(this).data("redirect").replace(/PROJECT_ID/g, $(this).val());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Datepicker translation
|
|
||||||
$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);
|
|
||||||
|
|
||||||
// Alert box fadeout
|
|
||||||
$(".alert-fade-out").delay(4000).fadeOut(800, function() {
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
// Datetime picker
|
|
||||||
$(".form-datetime").datetimepicker({
|
|
||||||
controlType: 'select',
|
|
||||||
oneLine: true,
|
|
||||||
dateFormat: 'yy-mm-dd',
|
|
||||||
// timeFormat: 'h:mm tt',
|
|
||||||
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
|
|
||||||
$(".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 < 1 ? "align-left" : "align-right")
|
|
||||||
.appendTo(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Screenshot
|
|
||||||
if (Kanboard.Exists("screenshot-zone")) {
|
|
||||||
Kanboard.Screenshot.Init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
@@ -1,187 +1,49 @@
|
|||||||
(function() {
|
function Board() {
|
||||||
|
this.app = null;
|
||||||
var checkInterval = null;
|
this.checkInterval = null;
|
||||||
|
|
||||||
function on_popover(e)
|
|
||||||
{
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
Kanboard.Popover(e, Kanboard.InitAfterAjax);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyboard_shortcuts()
|
Board.prototype.execute = function(app) {
|
||||||
{
|
this.app = app;
|
||||||
Mousetrap.bind("n", function() {
|
this.app.swimlane.refresh();
|
||||||
Kanboard.OpenPopover(
|
this.app.swimlane.listen();
|
||||||
$("#board").data("task-creation-url"),
|
this.poll();
|
||||||
Kanboard.InitAfterAjax
|
this.keyboardShortcuts();
|
||||||
);
|
this.resizeColumnHeight();
|
||||||
});
|
this.listen();
|
||||||
|
this.dragAndDrop();
|
||||||
|
this.compactView();
|
||||||
|
|
||||||
Mousetrap.bind("s", function() {
|
$(window).resize(this.resizeColumnHeight);
|
||||||
Kanboard.ShowLoadingIcon();
|
};
|
||||||
|
|
||||||
$.ajax({
|
Board.prototype.poll = function() {
|
||||||
cache: false,
|
|
||||||
url: $('.filter-display-mode:not([style="display: none;"]) a').attr('href'),
|
|
||||||
success: function(data) {
|
|
||||||
$("#board-container").remove();
|
|
||||||
$("#main").append(data);
|
|
||||||
Kanboard.InitAfterAjax();
|
|
||||||
board_unload_events();
|
|
||||||
board_load_events();
|
|
||||||
compactview_reload();
|
|
||||||
$('.filter-display-mode').toggle();
|
|
||||||
Kanboard.HideLoadingIcon();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Mousetrap.bind("c", function() {
|
|
||||||
compactview_toggle();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function resize_column()
|
|
||||||
{
|
|
||||||
var position = $(".board-swimlane").position();
|
|
||||||
$(".board-task-list").height($(window).height() - position.top);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the board
|
|
||||||
function board_load_events()
|
|
||||||
{
|
|
||||||
// Resize column height
|
|
||||||
resize_column();
|
|
||||||
|
|
||||||
// Drag and drop
|
|
||||||
$(".board-task-list").sortable({
|
|
||||||
delay: 300,
|
|
||||||
distance: 5,
|
|
||||||
connectWith: ".column",
|
|
||||||
placeholder: "draggable-placeholder",
|
|
||||||
items: ".draggable-item",
|
|
||||||
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 < 1 ? "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-spinner fa-spin"></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"));
|
var interval = parseInt($("#board").attr("data-check-interval"));
|
||||||
|
|
||||||
if (interval > 0) {
|
if (interval > 0) {
|
||||||
checkInterval = window.setInterval(board_check, interval * 1000);
|
this.checkInterval = window.setInterval(this.check.bind(this), interval * 1000);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Stop events
|
Board.prototype.check = function() {
|
||||||
function board_unload_events()
|
if (this.app.isVisible()) {
|
||||||
{
|
|
||||||
clearInterval(checkInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save and refresh the board
|
var self = this;
|
||||||
function board_save(taskId, columnId, position, swimlaneId)
|
this.app.showLoadingIcon();
|
||||||
{
|
|
||||||
board_unload_events();
|
$.ajax({
|
||||||
Kanboard.ShowLoadingIcon();
|
cache: false,
|
||||||
|
url: $("#board").attr("data-check-url"),
|
||||||
|
statusCode: {
|
||||||
|
200: function(data) { self.refresh(data); },
|
||||||
|
304: function () { self.app.hideLoadingIcon(); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Board.prototype.save = function(taskId, columnId, position, swimlaneId) {
|
||||||
|
this.app.showLoadingIcon();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
@@ -195,59 +57,72 @@
|
|||||||
"swimlane_id": swimlaneId,
|
"swimlane_id": swimlaneId,
|
||||||
"position": position
|
"position": position
|
||||||
}),
|
}),
|
||||||
success: function(data) {
|
success: this.refresh.bind(this),
|
||||||
$("#board-container").remove();
|
error: this.app.hideLoadingIcon.bind(this)
|
||||||
$("#main").append(data);
|
});
|
||||||
Kanboard.InitAfterAjax();
|
};
|
||||||
board_load_events();
|
|
||||||
compactview_reload();
|
Board.prototype.refresh = function(data) {
|
||||||
Kanboard.HideLoadingIcon();
|
$("#board-container").replaceWith(data);
|
||||||
|
|
||||||
|
this.app.listen();
|
||||||
|
this.app.swimlane.refresh();
|
||||||
|
this.app.swimlane.listen();
|
||||||
|
this.resizeColumnHeight();
|
||||||
|
this.app.hideLoadingIcon();
|
||||||
|
this.listen();
|
||||||
|
this.dragAndDrop();
|
||||||
|
this.compactView();
|
||||||
|
};
|
||||||
|
|
||||||
|
Board.prototype.resizeColumnHeight = function() {
|
||||||
|
var position = $(".board-swimlane").position();
|
||||||
|
|
||||||
|
if (position) {
|
||||||
|
$(".board-task-list").height($(window).height() - position.top);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Board.prototype.dragAndDrop = function() {
|
||||||
|
var self = this;
|
||||||
|
$(".board-task-list").sortable({
|
||||||
|
delay: 300,
|
||||||
|
distance: 5,
|
||||||
|
connectWith: ".board-task-list",
|
||||||
|
placeholder: "draggable-placeholder",
|
||||||
|
items: ".draggable-item",
|
||||||
|
stop: function(event, ui) {
|
||||||
|
self.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')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// Check if the board have been changed by someone else
|
Board.prototype.listen = function() {
|
||||||
function board_check()
|
var self = this;
|
||||||
{
|
|
||||||
if (Kanboard.IsVisible()) {
|
$(document).on("click", ".task-board", function() {
|
||||||
$.ajax({
|
window.location = $(this).data("task-url");
|
||||||
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();
|
|
||||||
compactview_reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function compactview_load_events()
|
$(document).on('click', ".filter-toggle-scrolling", function(e) {
|
||||||
{
|
|
||||||
jQuery(document).on('click', ".filter-toggle-scrolling", function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
compactview_toggle();
|
self.toggleCompactView();
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
compactview_reload();
|
Board.prototype.toggleCompactView = function() {
|
||||||
}
|
var scrolling = localStorage.getItem("horizontal_scroll") || 1;
|
||||||
|
localStorage.setItem("horizontal_scroll", scrolling == 0 ? 1 : 0);
|
||||||
function compactview_toggle()
|
this.compactView();
|
||||||
{
|
};
|
||||||
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) {
|
|
||||||
|
|
||||||
|
Board.prototype.compactView = function() {
|
||||||
|
if (localStorage.getItem("horizontal_scroll") == 0) {
|
||||||
$(".filter-wide").show();
|
$(".filter-wide").show();
|
||||||
$(".filter-compact").hide();
|
$(".filter-compact").hide();
|
||||||
|
|
||||||
@@ -255,24 +130,35 @@
|
|||||||
$("#board th").addClass("board-column-compact");
|
$("#board th").addClass("board-column-compact");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
$(".filter-wide").hide();
|
$(".filter-wide").hide();
|
||||||
$(".filter-compact").show();
|
$(".filter-compact").show();
|
||||||
|
|
||||||
$("#board-container").removeClass("board-container-compact");
|
$("#board-container").removeClass("board-container-compact");
|
||||||
$("#board th").removeClass("board-column-compact");
|
$("#board th").removeClass("board-column-compact");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
Board.prototype.toggleCollapsedMode = function() {
|
||||||
|
var self = this;
|
||||||
|
this.app.showLoadingIcon();
|
||||||
|
|
||||||
if (Kanboard.Exists("board")) {
|
$.ajax({
|
||||||
board_load_events();
|
cache: false,
|
||||||
compactview_load_events();
|
url: $('.filter-display-mode:not([style="display: none;"]) a').attr('href'),
|
||||||
keyboard_shortcuts();
|
success: function(data) {
|
||||||
|
$('.filter-display-mode').toggle();
|
||||||
$(window).resize(resize_column);
|
self.refresh(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
Board.prototype.keyboardShortcuts = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
Mousetrap.bind("c", function() { self.toggleCompactView(); });
|
||||||
|
Mousetrap.bind("s", function() { self.toggleCollapsedMode(); });
|
||||||
|
|
||||||
|
Mousetrap.bind("n", function() {
|
||||||
|
self.app.popover.open($("#board").data("task-creation-url"));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
(function() {
|
function Calendar() {
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
}
|
||||||
if (Kanboard.Exists("calendar")) {
|
|
||||||
|
Calendar.prototype.execute = function() {
|
||||||
var calendar = $('#calendar');
|
var calendar = $('#calendar');
|
||||||
|
|
||||||
calendar.fullCalendar({
|
calendar.fullCalendar({
|
||||||
@@ -45,7 +46,4 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
Kanboard.Screenshot = (function() {
|
function Screenshot() {
|
||||||
|
this.pasteCatcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
var pasteCatcher = null;
|
Screenshot.prototype.execute = function() {
|
||||||
|
this.initialize();
|
||||||
|
};
|
||||||
|
|
||||||
// Setup event listener and workarounds
|
// Setup event listener and workarounds
|
||||||
function init()
|
Screenshot.prototype.initialize = function() {
|
||||||
{
|
this.destroy();
|
||||||
destroy();
|
|
||||||
|
|
||||||
if (! window.Clipboard) {
|
if (! window.Clipboard) {
|
||||||
|
|
||||||
// Create a contenteditable element
|
// Create a contenteditable element
|
||||||
pasteCatcher = document.createElement("div");
|
this.pasteCatcher = document.createElement("div");
|
||||||
pasteCatcher.id = "screenshot-pastezone";
|
this.pasteCatcher.id = "screenshot-pastezone";
|
||||||
pasteCatcher.contentEditable = "true";
|
this.pasteCatcher.contentEditable = "true";
|
||||||
|
|
||||||
// Insert the content editable at the top to avoid scrolling down in the board view
|
// Insert the content editable at the top to avoid scrolling down in the board view
|
||||||
pasteCatcher.style.opacity = 0;
|
this.pasteCatcher.style.opacity = 0;
|
||||||
pasteCatcher.style.position = "fixed";
|
this.pasteCatcher.style.position = "fixed";
|
||||||
pasteCatcher.style.top = 0;
|
this.pasteCatcher.style.top = 0;
|
||||||
pasteCatcher.style.right = 0;
|
this.pasteCatcher.style.right = 0;
|
||||||
pasteCatcher.style.width = 0;
|
this.pasteCatcher.style.width = 0;
|
||||||
|
|
||||||
document.body.insertBefore(pasteCatcher, document.body.firstChild);
|
document.body.insertBefore(this.pasteCatcher, document.body.firstChild);
|
||||||
|
|
||||||
// Set focus on the contenteditable element
|
// Set focus on the contenteditable element
|
||||||
pasteCatcher.focus();
|
this.pasteCatcher.focus();
|
||||||
|
|
||||||
// Set the focus when clicked anywhere in the document
|
// Set the focus when clicked anywhere in the document
|
||||||
document.addEventListener("click", setFocus);
|
document.addEventListener("click", this.setFocus.bind(this));
|
||||||
|
|
||||||
// Set the focus when clicked in screenshot dropzone (popover)
|
// Set the focus when clicked in screenshot dropzone (popover)
|
||||||
document.getElementById("screenshot-zone").addEventListener("click", setFocus);
|
document.getElementById("screenshot-zone").addEventListener("click", this.setFocus.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("paste", pasteHandler);
|
window.addEventListener("paste", this.pasteHandler.bind(this));
|
||||||
}
|
};
|
||||||
|
|
||||||
// Set focus on the contentEditable element
|
// Destroy contentEditable element
|
||||||
function setFocus()
|
Screenshot.prototype.destroy = function() {
|
||||||
{
|
if (this.pasteCatcher != null) {
|
||||||
if (pasteCatcher !== null) {
|
document.body.removeChild(this.pasteCatcher);
|
||||||
pasteCatcher.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy contenteditable
|
|
||||||
function destroy()
|
|
||||||
{
|
|
||||||
if (pasteCatcher != null) {
|
|
||||||
document.body.removeChild(pasteCatcher);
|
|
||||||
}
|
}
|
||||||
else if (document.getElementById("screenshot-pastezone")) {
|
else if (document.getElementById("screenshot-pastezone")) {
|
||||||
document.body.removeChild(document.getElementById("screenshot-pastezone"));
|
document.body.removeChild(document.getElementById("screenshot-pastezone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
document.removeEventListener("click", setFocus);
|
document.removeEventListener("click", this.setFocus.bind(this));
|
||||||
pasteCatcher = null;
|
this.pasteCatcher = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set focus on contentEditable element
|
||||||
|
Screenshot.prototype.setFocus = function() {
|
||||||
|
if (this.pasteCatcher !== null) {
|
||||||
|
this.pasteCatcher.focus();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Paste event callback
|
// Paste event callback
|
||||||
function pasteHandler(e)
|
Screenshot.prototype.pasteHandler = function(e) {
|
||||||
{
|
|
||||||
// Firefox doesn't have the property e.clipboardData.items (only Chrome)
|
// Firefox doesn't have the property e.clipboardData.items (only Chrome)
|
||||||
if (e.clipboardData && e.clipboardData.items) {
|
if (e.clipboardData && e.clipboardData.items) {
|
||||||
|
|
||||||
@@ -77,8 +77,9 @@ Kanboard.Screenshot = (function() {
|
|||||||
|
|
||||||
// Get the image as base64 data
|
// Get the image as base64 data
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
var self = this;
|
||||||
reader.onload = function(event) {
|
reader.onload = function(event) {
|
||||||
createImage(event.target.result);
|
self.createImage(event.target.result);
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
@@ -89,29 +90,27 @@ Kanboard.Screenshot = (function() {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
// Handle Firefox
|
// Handle Firefox
|
||||||
setTimeout(checkInput, 100);
|
setTimeout(this.checkInput.bind(this), 100);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Parse the input in the paste catcher element
|
// Parse the input in the paste catcher element
|
||||||
function checkInput()
|
Screenshot.prototype.checkInput = function() {
|
||||||
{
|
var child = this.pasteCatcher.childNodes[0];
|
||||||
var child = pasteCatcher.childNodes[0];
|
|
||||||
|
|
||||||
if (child) {
|
if (child) {
|
||||||
// If the user pastes an image, the src attribute
|
// If the user pastes an image, the src attribute
|
||||||
// will represent the image as a base64 encoded string.
|
// will represent the image as a base64 encoded string.
|
||||||
if (child.tagName === "IMG") {
|
if (child.tagName === "IMG") {
|
||||||
createImage(child.src);
|
this.createImage(child.src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pasteCatcher.innerHTML = "";
|
pasteCatcher.innerHTML = "";
|
||||||
}
|
};
|
||||||
|
|
||||||
// Creates a new image from a given source
|
// Creates a new image from a given source
|
||||||
function createImage(blob)
|
Screenshot.prototype.createImage = function(blob) {
|
||||||
{
|
|
||||||
var pastedImage = new Image();
|
var pastedImage = new Image();
|
||||||
pastedImage.src = blob;
|
pastedImage.src = blob;
|
||||||
|
|
||||||
@@ -127,19 +126,6 @@ Kanboard.Screenshot = (function() {
|
|||||||
zone.className = "screenshot-pasted";
|
zone.className = "screenshot-pasted";
|
||||||
zone.appendChild(pastedImage);
|
zone.appendChild(pastedImage);
|
||||||
|
|
||||||
destroy();
|
this.destroy();
|
||||||
init();
|
this.initialize();
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
|
||||||
|
|
||||||
if (Kanboard.Exists("screenshot-zone")) {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
Init: init,
|
|
||||||
Destroy: destroy
|
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
@@ -1,90 +1,67 @@
|
|||||||
(function() {
|
function Swimlane() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Swimlane.prototype.getStorageKey = function() {
|
||||||
|
return "hidden_swimlanes_" + $("#board").data("project-id");
|
||||||
|
};
|
||||||
|
|
||||||
|
Swimlane.prototype.expand = function(swimlaneId) {
|
||||||
|
var swimlaneIds = this.getAllCollapsed();
|
||||||
|
var index = swimlaneIds.indexOf(swimlaneId);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
swimlaneIds.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(this.getStorageKey(), JSON.stringify(swimlaneIds));
|
||||||
|
|
||||||
// Expand a Swimlane via display attributes
|
|
||||||
function expand(swimlaneId)
|
|
||||||
{
|
|
||||||
$('.swimlane-row-' + swimlaneId).css('display', 'table-row');
|
$('.swimlane-row-' + swimlaneId).css('display', 'table-row');
|
||||||
$('.show-icon-swimlane-' + swimlaneId).css('display', 'none');
|
$('.show-icon-swimlane-' + swimlaneId).css('display', 'none');
|
||||||
$('.hide-icon-swimlane-' + swimlaneId).css('display', 'inline');
|
$('.hide-icon-swimlane-' + swimlaneId).css('display', 'inline');
|
||||||
|
};
|
||||||
|
|
||||||
|
Swimlane.prototype.collapse = function(swimlaneId) {
|
||||||
|
var swimlaneIds = this.getAllCollapsed();
|
||||||
|
|
||||||
|
if (swimlaneIds.indexOf(swimlaneId) < 0) {
|
||||||
|
swimlaneIds.push(swimlaneId);
|
||||||
|
localStorage.setItem(this.getStorageKey(), JSON.stringify(swimlaneIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collapse a Swimlane via display attributes
|
|
||||||
function collapse(swimlaneId)
|
|
||||||
{
|
|
||||||
$('.swimlane-row-' + swimlaneId).css('display', 'none');
|
$('.swimlane-row-' + swimlaneId).css('display', 'none');
|
||||||
$('.show-icon-swimlane-' + swimlaneId).css('display', 'inline');
|
$('.show-icon-swimlane-' + swimlaneId).css('display', 'inline');
|
||||||
$('.hide-icon-swimlane-' + swimlaneId).css('display', 'none');
|
$('.hide-icon-swimlane-' + swimlaneId).css('display', 'none');
|
||||||
|
};
|
||||||
|
|
||||||
|
Swimlane.prototype.isCollapsed = function(swimlaneId) {
|
||||||
|
return this.getAllCollapsed().indexOf(swimlaneId) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
Swimlane.prototype.getAllCollapsed = function() {
|
||||||
|
return JSON.parse(localStorage.getItem(this.getStorageKey())) || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
Swimlane.prototype.refresh = function() {
|
||||||
|
var swimlaneIds = this.getAllCollapsed();
|
||||||
|
|
||||||
|
for (var i = 0; i < swimlaneIds.length; i++) {
|
||||||
|
this.collapse(swimlaneIds[i]);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Add swimlane Id to the hidden list and stores the list to localStorage
|
Swimlane.prototype.listen = function() {
|
||||||
function hide(id)
|
var self = this;
|
||||||
{
|
|
||||||
var storageKey = "hidden_swimlanes_" + $("#board").data("project-id");
|
|
||||||
var hiddenSwimlaneIds = JSON.parse(Kanboard.GetStorageItem(storageKey)) || [];
|
|
||||||
|
|
||||||
hiddenSwimlaneIds.push(id);
|
$(document).on('click', ".board-swimlane-toggle", function(e) {
|
||||||
|
|
||||||
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();
|
e.preventDefault();
|
||||||
|
|
||||||
var swimlaneId = $(this).data('swimlane-id');
|
var swimlaneId = $(this).data('swimlane-id');
|
||||||
|
|
||||||
if (isHidden(swimlaneId)) {
|
if (self.isCollapsed(swimlaneId)) {
|
||||||
unhide(swimlaneId);
|
self.expand(swimlaneId);
|
||||||
expand(swimlaneId);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hide(swimlaneId);
|
self.collapse(swimlaneId);
|
||||||
collapse(swimlaneId);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
})();
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ print_css="print links table board task comment subtask markdown"
|
|||||||
app_css="base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters"
|
app_css="base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters"
|
||||||
vendor_css="jquery-ui.min jquery-ui-timepicker-addon.min chosen.min fullcalendar.min font-awesome.min c3.min"
|
vendor_css="jquery-ui.min jquery-ui-timepicker-addon.min chosen.min fullcalendar.min font-awesome.min c3.min"
|
||||||
|
|
||||||
app_js="base board calendar analytic swimlane screenshot"
|
app_js="Popover Tooltip Markdown Sidebar Search App Screenshot Calendar Board Swimlane TaskRepartitionChart UserRepartitionChart CumulativeFlowDiagram BurndownChart BudgetChart AvgTimeColumnChart TaskTimeColumnChart LeadCycleTimeChart Router"
|
||||||
vendor_js="jquery-1.11.1.min jquery-ui.min jquery-ui-timepicker-addon.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min app.min"
|
vendor_js="jquery-1.11.1.min jquery-ui.min jquery-ui-timepicker-addon.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min app.min"
|
||||||
lang_js="da de es fi fr hu it ja nl pl pt-br ru sv sr th tr zh-cn"
|
lang_js="da de es fi fr hu it ja nl pl pt-br ru sv sr th tr zh-cn"
|
||||||
|
|
||||||
@@ -34,7 +34,9 @@ function minify_js {
|
|||||||
|
|
||||||
rm -f $dst_file $tmp_file 2>/dev/null
|
rm -f $dst_file $tmp_file 2>/dev/null
|
||||||
|
|
||||||
|
echo "(function() { 'use strict';" > $tmp_file;
|
||||||
for file in $app_js; do cat "assets/js/src/${file}.js" >> $tmp_file; done
|
for file in $app_js; do cat "assets/js/src/${file}.js" >> $tmp_file; done
|
||||||
|
echo "})();" >> $tmp_file;
|
||||||
|
|
||||||
curl -s \
|
curl -s \
|
||||||
-d compilation_level=SIMPLE_OPTIMIZATIONS \
|
-d compilation_level=SIMPLE_OPTIMIZATIONS \
|
||||||
|
|||||||
Reference in New Issue
Block a user