summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-08-04 22:51:44 -0400
committerFrederic Guillot <fred@kanboard.net>2015-08-04 22:52:12 -0400
commite13872fc2e46976a668454d7528b0e7daef85d52 (patch)
tree5485d244f0dd4865720dc39e78cd4d1ff559f6e5
parentf04ec0700cb111baabc49febf22425612a5b7c58 (diff)
Javascript refactoring
-rw-r--r--app/Model/ProjectAnalytic.php6
-rw-r--r--app/Template/board/table_swimlane.php2
-rw-r--r--app/Template/board/task_footer.php16
-rw-r--r--app/Template/board/task_menu.php16
-rw-r--r--app/Template/board/task_private.php2
-rw-r--r--assets/js/app.js72
-rw-r--r--assets/js/src/App.js196
-rw-r--r--assets/js/src/AvgTimeColumnChart.js39
-rw-r--r--assets/js/src/BudgetChart.js55
-rw-r--r--assets/js/src/BurndownChart.js48
-rw-r--r--assets/js/src/CumulativeFlowDiagram.js48
-rw-r--r--assets/js/src/LeadCycleTimeChart.js45
-rw-r--r--assets/js/src/Markdown.js50
-rw-r--r--assets/js/src/Popover.js50
-rw-r--r--assets/js/src/Router.js34
-rw-r--r--assets/js/src/Search.js61
-rw-r--r--assets/js/src/Sidebar.js25
-rw-r--r--assets/js/src/TaskRepartitionChart.js18
-rw-r--r--assets/js/src/TaskTimeColumnChart.js39
-rw-r--r--assets/js/src/Tooltip.js87
-rw-r--r--assets/js/src/UserRepartitionChart.js18
-rw-r--r--assets/js/src/analytic.js355
-rw-r--r--assets/js/src/base.js394
-rw-r--r--assets/js/src/board.js392
-rw-r--r--assets/js/src/calendar.js86
-rw-r--r--assets/js/src/screenshot.js204
-rw-r--r--assets/js/src/swimlane.js111
-rwxr-xr-xscripts/make-assets.sh4
28 files changed, 1195 insertions, 1278 deletions
diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php
index 8ac22626..dbca4eed 100644
--- a/app/Model/ProjectAnalytic.php
+++ b/app/Model/ProjectAnalytic.php
@@ -167,8 +167,10 @@ class ProjectAnalytic extends Base
$sums[$task['column_id']] += ($task['date_completed'] ?: time()) - $task['date_moved'];
foreach ($sums as $column_id => $time_spent) {
- $stats[$column_id]['count']++;
- $stats[$column_id]['time_spent'] += $time_spent;
+ if (isset($stats[$column_id])) {
+ $stats[$column_id]['count']++;
+ $stats[$column_id]['time_spent'] += $time_spent;
+ }
}
}
diff --git a/app/Template/board/table_swimlane.php b/app/Template/board/table_swimlane.php
index f9e4c5c1..77ec7423 100644
--- a/app/Template/board/table_swimlane.php
+++ b/app/Template/board/table_swimlane.php
@@ -17,7 +17,7 @@
<th class="board-column-header">
<?php if (! $not_editable): ?>
<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>
<?php endif ?>
diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php
index 69bf97c1..cadcb2d6 100644
--- a/app/Template/board/task_footer.php
+++ b/app/Template/board/task_footer.php
@@ -10,7 +10,7 @@
'changeCategory',
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
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')
) ?>
<?php endif ?>
@@ -27,31 +27,31 @@
<?php endif ?>
<?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 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 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>&nbsp;<?= $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>&nbsp;<?= $task['nb_links'] ?></span>
<?php endif ?>
<?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>&nbsp;<?= 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>&nbsp;<?= round($task['nb_completed_subtasks']/$task['nb_subtasks']*100, 0).'%' ?></span>
<?php endif ?>
<?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>&nbsp;<?= $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>&nbsp;<?= $task['nb_files'] ?></span>
<?php endif ?>
<?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>&nbsp;<?= $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>&nbsp;<?= $task['nb_comments'] ?></span>
<?php endif ?>
<?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>
</span>
<?php endif ?>
diff --git a/app/Template/board/task_menu.php b/app/Template/board/task_menu.php
index 71963b5e..9307b76a 100644
--- a/app/Template/board/task_menu.php
+++ b/app/Template/board/task_menu.php
@@ -2,14 +2,14 @@
<span>
<a href="#" class="dropdown-menu"><?= '#'.$task['id'] ?></a>
<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-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-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-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-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-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-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-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-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, '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, '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, '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, 'popover') ?></li>
</ul>
</span>
</span>
diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php
index 7eaff580..564fd176 100644
--- a/app/Template/board/task_private.php
+++ b/app/Template/board/task_private.php
@@ -37,7 +37,7 @@
'changeAssignee',
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
false,
- 'task-board-popover',
+ 'popover',
t('Change assignee')
) ?>
</span>
diff --git a/assets/js/app.js b/assets/js/app.js
index af664c59..63d42e53 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -140,39 +140,41 @@ c,a,e),l[d.key][c?"unshift":"push"]({callback:b,modifiers:d.modifiers,action:d.a
unbind:function(a,b){return m.bind(a,function(){},b)},trigger:function(a,b){if(q[a+":"+b])q[a+":"+b]({},a);return this},reset:function(){l={};q={};return this},stopCallback:function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},handleKey:function(a,b,d){var c=C(a,b,d),e;b={};var f=0,g=!1;for(e=0;e<c.length;++e)c[e].seq&&(f=Math.max(f,c[e].level));for(e=0;e<c.length;++e)c[e].seq?c[e].level==f&&(g=!0,
b[c[e].seq]=1,x(c[e].callback,d,c[e].combo,c[e].seq)):g||x(c[e].callback,d,c[e].combo);c="keypress"==d.type&&I;d.type!=u||w(a)||c||t(b);I=g&&"keydown"==d.type}};J.Mousetrap=m;"function"===typeof define&&define.amd&&define(m)})(window,document);
Mousetrap=function(a){var d={},e=a.stopCallback;a.stopCallback=function(b,c,a){return d[a]?!1:e(b,c,a)};a.bindGlobal=function(b,c,e){a.bind(b,c,e);if(b instanceof Array)for(c=0;c<b.length;c++)d[b[c]]=!0;else d[b]=!0};return a}(Mousetrap);
-var Kanboard=function(){jQuery(document).ready(function(){Kanboard.Init()});return{ShowLoadingIcon:function(){$("body").append('<span id="app-loading-icon">&nbsp;<i class="fa fa-spinner fa-spin"></i></span>')},HideLoadingIcon:function(){$("#app-loading-icon").remove()},Exists:function(c){return document.getElementById(c)?!0:!1},Popover:function(c,a){c.preventDefault();c.stopPropagation();var b=c.target.getAttribute("href");b||(b=c.target.getAttribute("data-href"));b&&Kanboard.OpenPopover(b,a)},OpenPopover:function(c,
-a){$.get(c,function(b){$("body").append('<div id="popover-container"><div id="popover-content">'+b+"</div></div>");$("#popover-container").click(function(){Kanboard.ClosePopover()});$("#popover-content").click(function(a){a.stopPropagation()});$(".close-popover").click(function(a){a.preventDefault();Kanboard.ClosePopover()});Mousetrap.bindGlobal("esc",function(){Kanboard.ClosePopover()});a&&a()})},ClosePopover:function(){$("#popover-container").remove();Kanboard.Screenshot.Destroy()},IsVisible:function(){var c=
-"";"undefined"!==typeof document.hidden?c="visibilityState":"undefined"!==typeof document.mozHidden?c="mozVisibilityState":"undefined"!==typeof document.msHidden?c="msVisibilityState":"undefined"!==typeof document.webkitHidden&&(c="webkitVisibilityState");return""!=c?"visible"==document[c]:!0},SetStorageItem:function(c,a){"undefined"!==typeof Storage&&localStorage.setItem(c,a)},GetStorageItem:function(c){return"undefined"!==typeof Storage?localStorage.getItem(c):""},MarkdownPreview:function(c){c.preventDefault();
-var a=$(this),b=$(this).closest("ul"),d=$(".write-area"),g=$(".preview-area"),h=$("textarea");$.ajax({url:"?controller=app&action=preview",contentType:"application/json",type:"POST",processData:!1,dataType:"html",data:JSON.stringify({text:h.val()})}).done(function(c){b.find("li").removeClass("form-tab-selected");a.parent().addClass("form-tab-selected");g.find(".markdown").html(c);g.css("height",h.css("height"));g.css("width",h.css("width"));d.hide();g.show()})},MarkdownWriter:function(c){c.preventDefault();
-$(this).closest("ul").find("li").removeClass("form-tab-selected");$(this).parent().addClass("form-tab-selected");$(".write-area").show();$(".preview-area").hide()},CheckSession:function(){$(".form-login").length||$.ajax({cache:!1,url:$("body").data("status-url"),statusCode:{401:function(){window.location=$("body").data("login-url")}}})},Init: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())});window.setInterval(Kanboard.CheckSession,6E4);Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(a){a.preventDefault();$("#board-selector").trigger("chosen:open")});Mousetrap.bind("f",function(a){a.preventDefault();(a=document.getElementById("form-search"))&&a.focus()});Mousetrap.bind("v b",
-function(a){a=$(".view-board");a.length&&(window.location=a.attr("href"))});Mousetrap.bind("v c",function(a){a=$(".view-calendar");a.length&&(window.location=a.attr("href"))});Mousetrap.bind("v l",function(a){a=$(".view-listing");a.length&&(window.location=a.attr("href"))});$(document).on("focus","#form-search",function(){$("#form-search")[0].setSelectionRange&&$("#form-search")[0].setSelectionRange($("#form-search").val().length,$("#form-search").val().length)});$(document).on("click",".filter-helper",
-function(a){a.preventDefault();$("#form-search").val($(this).data("filter"));$("form.search").submit()});$(document).on("click",".sidebar-collapse",function(a){a.preventDefault();$(".sidebar-container").addClass("sidebar-collapsed");$(".sidebar-expand").show();$(".sidebar h2").hide();$(".sidebar ul").hide();$(".sidebar-collapse").hide()});$(document).on("click",".sidebar-expand",function(a){a.preventDefault();$(".sidebar-container").removeClass("sidebar-collapsed");$(".sidebar-collapse").show();$(".sidebar h2").show();
-$(".sidebar ul").show();$(".sidebar-expand").hide()});var c=!1;$("select.task-reload-project-destination").change(function(){c||($(".loading-icon").show(),c=!0,window.location=$(this).data("redirect").replace(/PROJECT_ID/g,$(this).val()))});$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);$(".alert-fade-out").delay(4E3).fadeOut(800,function(){$(this).remove()});Kanboard.InitAfterAjax()},InitAfterAjax:function(){$(document).on("click",".popover",Kanboard.Popover);$("[autofocus]").each(function(c,
-a){$(this).focus()});$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$(".form-datetime").datetimepicker({controlType:"select",oneLine:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$("#markdown-preview").click(Kanboard.MarkdownPreview);$("#markdown-write").click(Kanboard.MarkdownWriter);$(".auto-select").focus(function(){$(this).select()});$(".dropit-submenu").hide();$(".dropdown").not(".dropit").dropit({triggerParentEl:"span"});$(".task-autocomplete").length&&
-(""==$(".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(c,a){var b=$(".task-autocomplete").data("dst-field");$("input[name="+b+"]").val(a.item.id);$(".task-autocomplete").parent().find("input[type=submit]").removeAttr("disabled")}}));$(".tooltip").tooltip({content:function(){return'<div class="markdown">'+$(this).attr("title")+
-"</div>"},position:{my:"left-20 top",at:"center bottom+9",using:function(c,a){$(this).css(c);var b=a.target.left+a.target.width/2-a.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(a.vertical).addClass(1>b?"align-left":"align-right").appendTo(this)}}});Kanboard.Exists("screenshot-zone")&&Kanboard.Screenshot.Init()}}}();
-(function(){function c(a){a.preventDefault();a.stopPropagation();Kanboard.Popover(a,Kanboard.InitAfterAjax)}function a(){Mousetrap.bind("n",function(){Kanboard.OpenPopover($("#board").data("task-creation-url"),Kanboard.InitAfterAjax)});Mousetrap.bind("s",function(){Kanboard.ShowLoadingIcon();$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();clearInterval(l);d();
-k();$(".filter-display-mode").toggle();Kanboard.HideLoadingIcon()}})});Mousetrap.bind("c",function(){e()})}function b(){var a=$(".board-swimlane").position();$(".board-task-list").height($(window).height()-a.top)}function d(){b();$(".board-task-list").sortable({delay:300,distance:5,connectWith:".column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(a,b){g(b.item.attr("data-task-id"),b.item.parent().attr("data-column-id"),b.item.index()+1,b.item.parent().attr("data-swimlane-id"))}});
-$("#board").on("click",".task-board-popover",c);$("#board").on("click",".task-board",function(){window.location=$(this).data("task-url")});$(".task-board-tooltip").tooltip({track:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(a,b){$(this).css(a);var e=b.target.left+b.target.width/2-b.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(b.vertical).addClass(1>e?"align-left":"align-right").appendTo(this)}},content:function(a){if(a=$(this).attr("data-href")){var b=this;$.get(a,
-function m(a){$(".ui-tooltip-content:visible").html(a);a=$(".ui-tooltip:visible");a.css({top:"",left:""});a.children(".tooltip-arrow").remove();var e=$(b).tooltip("option","position");e.of=$(b);a.position(e);$("#tooltip-subtasks a").not(".popover").click(function(a){a.preventDefault();a.stopPropagation();$(this).hasClass("popover-subtask-restriction")?(Kanboard.OpenPopover($(this).attr("href")),$(b).tooltip("close")):$.get($(this).attr("href"),m)})});return'<i class="fa fa-spinner fa-spin"></i>'}}}).on("mouseenter",
-function(){var a=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(a).tooltip("close")})}).on("mouseleave focusout",function(a){a.stopImmediatePropagation();var b=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(b).tooltip("close")},100)});var a=parseInt($("#board").attr("data-check-interval"));0<a&&(l=window.setInterval(h,1E3*a))}function g(a,b,e,c){clearInterval(l);Kanboard.ShowLoadingIcon();$.ajax({cache:!1,url:$("#board").attr("data-save-url"),contentType:"application/json",
-type:"POST",processData:!1,data:JSON.stringify({task_id:a,column_id:b,swimlane_id:c,position:e}),success:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();d();k();Kanboard.HideLoadingIcon()}})}function h(){Kanboard.IsVisible()&&$.ajax({cache:!1,url:$("#board").attr("data-check-url"),statusCode:{200:function(a){$("#board-container").remove();$("#main").append(a);Kanboard.InitAfterAjax();clearInterval(l);d();k()}}})}function f(){jQuery(document).on("click",".filter-toggle-scrolling",
-function(a){a.preventDefault();e()});k()}function e(){var a=Kanboard.GetStorageItem("horizontal_scroll")||1;Kanboard.SetStorageItem("horizontal_scroll",0==a?1:0);k()}function k(){0==Kanboard.GetStorageItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),
-$("#board th").removeClass("board-column-compact"))}var l=null;jQuery(document).ready(function(){Kanboard.Exists("board")&&(d(),f(),a(),$(window).resize(b))})})();
-(function(){jQuery(document).ready(function(){if(Kanboard.Exists("calendar")){var c=$("#calendar");c.fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(a){$.ajax({cache:!1,url:c.data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a.id,date_due:a.start.format()})})},viewRender:function(){var a=c.data("check-url"),
-b={start:c.fullCalendar("getView").start.format(),end:c.fullCalendar("getView").end.format()},d;for(d in b)a+="&"+d+"="+b[d];$.getJSON(a,function(a){c.fullCalendar("removeEvents");c.fullCalendar("addEventSource",a);c.fullCalendar("rerenderEvents")})}})}})})();
-(function(){function c(a){return 86400<=a?Math.round(a/86400)+"d":3600<=a?Math.round(a/3600)+"h":60<=a?Math.round(a/60)+"m":a+"s"}jQuery(document).ready(function(){if(Kanboard.Exists("analytic-task-repartition")){for(var a=$("#chart").data("metrics"),b=[],d=0;d<a.length;d++)b.push([a[d].column_title,a[d].nb_tasks]);c3.generate({data:{columns:b,type:"donut"}})}else if(Kanboard.Exists("analytic-user-repartition")){a=$("#chart").data("metrics");b=[];for(d=0;d<a.length;d++)b.push([a[d].user,a[d].nb_tasks]);
-c3.generate({data:{columns:b,type:"donut"}})}else if(Kanboard.Exists("analytic-cfd")){for(var a=$("#chart").data("metrics"),b=[],d=[],g=[],h=d3.time.format("%Y-%m-%d"),f=d3.time.format($("#chart").data("date-format")),e=0;e<a.length;e++)for(var k=0;k<a[e].length;k++)0==e?(b.push([a[e][k]]),0<k&&d.push(a[e][k])):(b[k].push(a[e][k]),0==k&&g.push(f(h.parse(a[e][k]))));c3.generate({data:{columns:b,type:"area-spline",groups:[d]},axis:{x:{type:"category",categories:g}}})}else if(Kanboard.Exists("analytic-burndown")){a=
-$("#chart").data("metrics");b=[[$("#chart").data("label-total")]];d=[];g=d3.time.format("%Y-%m-%d");h=d3.time.format($("#chart").data("date-format"));for(f=0;f<a.length;f++)for(e=0;e<a[f].length;e++)0==f?b.push([a[f][e]]):(b[e+1].push(a[f][e]),0<e&&(void 0==b[0][f]&&b[0].push(0),b[0][f]+=a[f][e]),0==e&&d.push(h(g.parse(a[f][e]))));c3.generate({data:{columns:b},axis:{x:{type:"category",categories:d}}})}else if(Kanboard.Exists("budget-chart")){a=[];b=$("#chart").data("metrics");e=$("#chart").data("labels");
-d=d3.time.format("%Y-%m-%d");g=d3.time.format($("#chart").data("date-format"));h=[[e["in"]],[e.left],[e.out]];f={};f[e["in"]]="#5858FA";f[e.left]="#04B404";f[e.out]="#DF3A01";for(e=0;e<b.length;e++)a.push(g(d.parse(b[e].date))),h[0].push(b[e]["in"]),h[1].push(b[e].left),h[2].push(b[e].out);c3.generate({data:{columns:h,colors:f,type:"bar"},bar:{width:{ratio:.25}},grid:{x:{show:!0},y:{show:!0}},axis:{x:{type:"category",categories:a}}})}else if(Kanboard.Exists("analytic-avg-time-column")){a=$("#chart").data("metrics");
-b=[$("#chart").data("label")];d=[];for(g in a)b.push(a[g].average),d.push(a[g].title);c3.generate({data:{columns:[b],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:d},y:{tick:{format:c}}},legend:{show:!1}})}else if(Kanboard.Exists("analytic-task-time-column")){a=$("#chart").data("metrics");b=[$("#chart").data("label")];d=[];for(g=0;g<a.length;g++)b.push(a[g].time_spent),d.push(a[g].title);c3.generate({data:{columns:[b],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",
-categories:d},y:{tick:{format:c}}},legend:{show:!1}})}else if(Kanboard.Exists("analytic-lead-cycle-time")){a=$("#chart").data("metrics");b=[$("#chart").data("label-cycle")];d=[$("#chart").data("label-lead")];g=[];h={};h[$("#chart").data("label-cycle")]="area";h[$("#chart").data("label-lead")]="area-spline";f={};f[$("#chart").data("label-lead")]="#afb42b";f[$("#chart").data("label-cycle")]="#4e342e";for(e=0;e<a.length;e++)b.push(parseInt(a[e].avg_cycle_time)),d.push(parseInt(a[e].avg_lead_time)),g.push(a[e].day);
-c3.generate({data:{columns:[d,b],types:h,colors:f},axis:{x:{type:"category",categories:g},y:{tick:{format:c}}}})}})})();
-(function(){function c(a){$(".swimlane-row-"+a).css("display","none");$(".show-icon-swimlane-"+a).css("display","inline");$(".hide-icon-swimlane-"+a).css("display","none")}function a(){var a="hidden_swimlanes_"+$("#board").data("project-id");return JSON.parse(Kanboard.GetStorageItem(a))||[]}jQuery(document).ajaxComplete(function(){a().map(function(a){c(a)})});jQuery(document).ready(function(){a().map(function(a){c(a)})});jQuery(document).on("click",".board-swimlane-toggle",function(b){b.preventDefault();
-b=$(this).data("swimlane-id");if(-1<a().indexOf(b)){var d="hidden_swimlanes_"+$("#board").data("project-id"),g=JSON.parse(Kanboard.GetStorageItem(d))||[],h=g.indexOf(b);-1<h&&g.splice(h,1);Kanboard.SetStorageItem(d,JSON.stringify(g));$(".swimlane-row-"+b).css("display","table-row");$(".show-icon-swimlane-"+b).css("display","none");$(".hide-icon-swimlane-"+b).css("display","inline")}else d="hidden_swimlanes_"+$("#board").data("project-id"),g=JSON.parse(Kanboard.GetStorageItem(d))||[],g.push(b),Kanboard.SetStorageItem(d,
-JSON.stringify(g)),c(b)})})();
-Kanboard.Screenshot=function(){function c(){b();window.Clipboard||(f=document.createElement("div"),f.id="screenshot-pastezone",f.contentEditable="true",f.style.opacity=0,f.style.position="fixed",f.style.top=0,f.style.right=0,f.style.width=0,document.body.insertBefore(f,document.body.firstChild),f.focus(),document.addEventListener("click",a),document.getElementById("screenshot-zone").addEventListener("click",a));window.addEventListener("paste",d)}function a(){null!==f&&f.focus()}function b(){null!=
-f?document.body.removeChild(f):document.getElementById("screenshot-pastezone")&&document.body.removeChild(document.getElementById("screenshot-pastezone"));document.removeEventListener("click",a);f=null}function d(a){if(a.clipboardData&&a.clipboardData.items){if(a=a.clipboardData.items)for(var b=0;b<a.length;b++)if(-1!==a[b].type.indexOf("image")){var c=a[b].getAsFile(),d=new FileReader;d.onload=function(a){h(a.target.result)};d.readAsDataURL(c)}}else setTimeout(g,100)}function g(){var a=f.childNodes[0];
-a&&"IMG"===a.tagName&&h(a.src);f.innerHTML=""}function h(a){var d=new Image;d.src=a;d.onload=function(){var b=a.split("base64,")[1];$("input[name=screenshot]").val(b)};var f=document.getElementById("screenshot-zone");f.innerHTML="";f.className="screenshot-pasted";f.appendChild(d);b();c()}var f=null;jQuery(document).ready(function(){Kanboard.Exists("screenshot-zone")&&c()});return{Init:c,Destroy:b}}();
+(function(){function p(a){this.app=a;this.router=new q;this.router.addRoute("screenshot-zone",m);Mousetrap.bindGlobal("esc",this.close)}function v(a){this.app=a}function r(){}function t(){}function u(){this.keyboardShortcuts()}function k(){this.popover=new p(this);this.markdown=new r;this.sidebar=new t;this.search=new u;this.tooltip=new v(this);this.swimlane=new n;this.keyboardShortcuts();this.boardSelector();this.listen();this.poll();$(".alert-fade-out").delay(4E3).fadeOut(800,function(){$(this).remove()});
+var a=!1;$("select.task-reload-project-destination").change(function(){a||($(".loading-icon").show(),a=!0,window.location=$(this).data("redirect").replace(/PROJECT_ID/g,$(this).val()))})}function m(){this.pasteCatcher=null}function w(){}function l(){this.checkInterval=this.app=null}function n(){}function x(){}function y(){}function z(){}function A(){}function B(){}function C(){}function D(){}function E(){}function q(){this.routes={}}p.prototype.isOpen=function(){return 0<$("#popover-container").size()};
+p.prototype.open=function(a){var b=this;$.get(a,function(a){$("body").append('<div id="popover-container"><div id="popover-content">'+a+"</div></div>");b.router.dispatch();b.app.listen()})};p.prototype.close=function(a){a&&a.preventDefault();$("#popover-container").remove()};p.prototype.onClick=function(a){a.preventDefault();a.stopPropagation();var b=a.target.getAttribute("href");b||(b=a.target.getAttribute("data-href"));b&&this.open(b)};p.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(a){a.stopPropagation()})};v.prototype.listen=function(){var a=this;$(".tooltip").tooltip({track:!1,show:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(a,c){$(this).css(a);var e=c.target.left+c.target.width/2-c.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(c.vertical).addClass(1>
+e?"align-left":"align-right").appendTo(this)}},content:function(){var b=this,c=$(this).attr("data-href");if(!c)return'<div class="markdown">'+$(this).attr("title")+"</div>";$.get(c,function f(c){var d=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(c);d.css({top:"",left:""});d.children(".tooltip-arrow").remove();c=$(b).tooltip("option","position");c.of=$(b);d.position(c);$("#tooltip-subtasks a").not(".popover").click(function(c){c.preventDefault();c.stopPropagation();$(this).hasClass("popover-subtask-restriction")?
+(a.app.popover.open($(this).attr("href")),$(b).tooltip("close")):$.get($(this).attr("href"),f)})});return'<i class="fa fa-spinner fa-spin"></i>'}}).on("mouseenter",function(){var a=this;$(this).tooltip("open");$(".ui-tooltip").on("mouseleave",function(){$(a).tooltip("close")})}).on("mouseleave focusout",function(a){a.stopImmediatePropagation();var c=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(c).tooltip("close")},100)})};r.prototype.showPreview=function(a){a.preventDefault();var b=
+$(this),c=$(this).closest("ul"),e=$(".write-area"),f=$(".preview-area"),g=$("textarea");$.ajax({url:"?controller=app&action=preview",contentType:"application/json",type:"POST",processData:!1,dataType:"html",data:JSON.stringify({text:g.val()})}).done(function(a){c.find("li").removeClass("form-tab-selected");b.parent().addClass("form-tab-selected");f.find(".markdown").html(a);f.css("height",g.css("height"));f.css("width",g.css("width"));e.hide();f.show()})};r.prototype.showWriter=function(a){a.preventDefault();
+$(this).closest("ul").find("li").removeClass("form-tab-selected");$(this).parent().addClass("form-tab-selected");$(".write-area").show();$(".preview-area").hide()};r.prototype.listen=function(){$(document).on("click","#markdown-preview",this.showPreview.bind(this));$(document).on("click","#markdown-write",this.showWriter.bind(this))};t.prototype.expand=function(a){a.preventDefault();$(".sidebar-container").removeClass("sidebar-collapsed");$(".sidebar-collapse").show();$(".sidebar h2").show();$(".sidebar ul").show();
+$(".sidebar-expand").hide()};t.prototype.collapse=function(a){a.preventDefault();$(".sidebar-container").addClass("sidebar-collapsed");$(".sidebar-expand").show();$(".sidebar h2").hide();$(".sidebar ul").hide();$(".sidebar-collapse").hide()};t.prototype.listen=function(){$(document).on("click",".sidebar-collapse",this.collapse);$(document).on("click",".sidebar-expand",this.expand)};u.prototype.focus=function(){$(document).on("focus","#form-search",function(){$("#form-search")[0].setSelectionRange&&
+$("#form-search")[0].setSelectionRange($("#form-search").val().length,$("#form-search").val().length)})};u.prototype.listen=function(){$(document).on("click",".filter-helper",function(a){a.preventDefault();$("#form-search").val($(this).data("filter"));$("form.search").submit()})};u.prototype.keyboardShortcuts=function(){Mousetrap.bind("v b",function(a){a=$(".view-board");a.length&&(window.location=a.attr("href"))});Mousetrap.bind("v c",function(a){a=$(".view-calendar");a.length&&(window.location=
+a.attr("href"))});Mousetrap.bind("v l",function(a){a=$(".view-listing");a.length&&(window.location=a.attr("href"))});Mousetrap.bind("f",function(a){a.preventDefault();(a=document.getElementById("form-search"))&&a.focus()})};k.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();$(".dropit-submenu").hide();$(".dropdown").not(".dropit").dropit({triggerParentEl:"span"})};
+k.prototype.focus=function(){$("[autofocus]").each(function(a,b){$(this).focus()});$(document).on("focus",".auto-select",function(){$(this).select()});$(document).on("mouseup",".auto-select",function(a){a.preventDefault()})};k.prototype.poll=function(){window.setInterval(this.checkSession,6E4)};k.prototype.keyboardShortcuts=function(){Mousetrap.bindGlobal("mod+enter",function(){$("form").submit()});Mousetrap.bind("b",function(a){a.preventDefault();$("#board-selector").trigger("chosen:open")})};k.prototype.checkSession=
+function(){$(".form-login").length||$.ajax({cache:!1,url:$("body").data("status-url"),statusCode:{401:function(){window.location=$("body").data("login-url")}}})};k.prototype.datePicker=function(){$.datepicker.setDefaults($.datepicker.regional[$("body").data("js-lang")]);$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:"yy-mm-dd",constrainInput:!1});$(".form-datetime").datetimepicker({controlType:"select",oneLine:!0,dateFormat:"yy-mm-dd",constrainInput:!1})};k.prototype.taskAutoComplete=
+function(){$(".task-autocomplete").length&&(""==$(".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(a,b){var c=$(".task-autocomplete").data("dst-field");$("input[name="+c+"]").val(b.item.id);$(".task-autocomplete").parent().find("input[type=submit]").removeAttr("disabled")}}))};k.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())})};k.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon">&nbsp;<i class="fa fa-spinner fa-spin"></i></span>')};k.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()};
+k.prototype.isVisible=function(){var a="";"undefined"!==typeof document.hidden?a="visibilityState":"undefined"!==typeof document.mozHidden?a="mozVisibilityState":"undefined"!==typeof document.msHidden?a="msVisibilityState":"undefined"!==typeof document.webkitHidden&&(a="webkitVisibilityState");return""!=a?"visible"==document[a]:!0};k.prototype.formatDuration=function(a){return 86400<=a?Math.round(a/86400)+"d":3600<=a?Math.round(a/3600)+"h":60<=a?Math.round(a/60)+"m":a+"s"};m.prototype.execute=function(){this.initialize()};
+m.prototype.initialize=function(){this.destroy();window.Clipboard||(this.pasteCatcher=document.createElement("div"),this.pasteCatcher.id="screenshot-pastezone",this.pasteCatcher.contentEditable="true",this.pasteCatcher.style.opacity=0,this.pasteCatcher.style.position="fixed",this.pasteCatcher.style.top=0,this.pasteCatcher.style.right=0,this.pasteCatcher.style.width=0,document.body.insertBefore(this.pasteCatcher,document.body.firstChild),this.pasteCatcher.focus(),document.addEventListener("click",
+this.setFocus.bind(this)),document.getElementById("screenshot-zone").addEventListener("click",this.setFocus.bind(this)));window.addEventListener("paste",this.pasteHandler.bind(this))};m.prototype.destroy=function(){null!=this.pasteCatcher?document.body.removeChild(this.pasteCatcher):document.getElementById("screenshot-pastezone")&&document.body.removeChild(document.getElementById("screenshot-pastezone"));document.removeEventListener("click",this.setFocus.bind(this));this.pasteCatcher=null};m.prototype.setFocus=
+function(){null!==this.pasteCatcher&&this.pasteCatcher.focus()};m.prototype.pasteHandler=function(a){if(a.clipboardData&&a.clipboardData.items){if(a=a.clipboardData.items)for(var b=0;b<a.length;b++)if(-1!==a[b].type.indexOf("image")){var c=a[b].getAsFile(),e=new FileReader,f=this;e.onload=function(a){f.createImage(a.target.result)};e.readAsDataURL(c)}}else setTimeout(this.checkInput.bind(this),100)};m.prototype.checkInput=function(){var a=this.pasteCatcher.childNodes[0];a&&"IMG"===a.tagName&&this.createImage(a.src);
+pasteCatcher.innerHTML=""};m.prototype.createImage=function(a){var b=new Image;b.src=a;b.onload=function(){var b=a.split("base64,")[1];$("input[name=screenshot]").val(b)};var c=document.getElementById("screenshot-zone");c.innerHTML="";c.className="screenshot-pasted";c.appendChild(b);this.destroy();this.initialize()};w.prototype.execute=function(){var a=$("#calendar");a.fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",
+right:"month,agendaWeek,agendaDay"},eventDrop:function(b){$.ajax({cache:!1,url:a.data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:b.id,date_due:b.start.format()})})},viewRender:function(){var b=a.data("check-url"),c={start:a.fullCalendar("getView").start.format(),end:a.fullCalendar("getView").end.format()},e;for(e in c)b+="&"+e+"="+c[e];$.getJSON(b,function(b){a.fullCalendar("removeEvents");a.fullCalendar("addEventSource",b);a.fullCalendar("rerenderEvents")})}})};
+l.prototype.execute=function(a){this.app=a;this.app.swimlane.refresh();this.app.swimlane.listen();this.poll();this.keyboardShortcuts();this.resizeColumnHeight();this.listen();this.dragAndDrop();this.compactView();$(window).resize(this.resizeColumnHeight)};l.prototype.poll=function(){var a=parseInt($("#board").attr("data-check-interval"));0<a&&(this.checkInterval=window.setInterval(this.check.bind(this),1E3*a))};l.prototype.check=function(){if(this.app.isVisible()){var a=this;this.app.showLoadingIcon();
+$.ajax({cache:!1,url:$("#board").attr("data-check-url"),statusCode:{200:function(b){a.refresh(b)},304:function(){a.app.hideLoadingIcon()}}})}};l.prototype.save=function(a,b,c,e){this.app.showLoadingIcon();$.ajax({cache:!1,url:$("#board").attr("data-save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:a,column_id:b,swimlane_id:e,position:c}),success:this.refresh.bind(this),error:this.app.hideLoadingIcon.bind(this)})};l.prototype.refresh=function(a){$("#board-container").replaceWith(a);
+this.app.listen();this.app.swimlane.refresh();this.app.swimlane.listen();this.resizeColumnHeight();this.app.hideLoadingIcon();this.listen();this.dragAndDrop();this.compactView()};l.prototype.resizeColumnHeight=function(){var a=$(".board-swimlane").position();a&&$(".board-task-list").height($(window).height()-a.top)};l.prototype.dragAndDrop=function(){var a=this;$(".board-task-list").sortable({delay:300,distance:5,connectWith:".board-task-list",placeholder:"draggable-placeholder",items:".draggable-item",
+stop:function(b,c){a.save(c.item.attr("data-task-id"),c.item.parent().attr("data-column-id"),c.item.index()+1,c.item.parent().attr("data-swimlane-id"))}})};l.prototype.listen=function(){var a=this;$(document).on("click",".task-board",function(){window.location=$(this).data("task-url")});$(document).on("click",".filter-toggle-scrolling",function(b){b.preventDefault();a.toggleCompactView()})};l.prototype.toggleCompactView=function(){var a=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",
+0==a?1:0);this.compactView()};l.prototype.compactView=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))};l.prototype.toggleCollapsedMode=function(){var a=this;this.app.showLoadingIcon();
+$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(b){$(".filter-display-mode").toggle();a.refresh(b)}})};l.prototype.keyboardShortcuts=function(){var a=this;Mousetrap.bind("c",function(){a.toggleCompactView()});Mousetrap.bind("s",function(){a.toggleCollapsedMode()});Mousetrap.bind("n",function(){a.app.popover.open($("#board").data("task-creation-url"))})};n.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")};
+n.prototype.expand=function(a){var b=this.getAllCollapsed(),c=b.indexOf(a);-1<c&&b.splice(c,1);localStorage.setItem(this.getStorageKey(),JSON.stringify(b));$(".swimlane-row-"+a).css("display","table-row");$(".show-icon-swimlane-"+a).css("display","none");$(".hide-icon-swimlane-"+a).css("display","inline")};n.prototype.collapse=function(a){var b=this.getAllCollapsed();0>b.indexOf(a)&&(b.push(a),localStorage.setItem(this.getStorageKey(),JSON.stringify(b)));$(".swimlane-row-"+a).css("display","none");
+$(".show-icon-swimlane-"+a).css("display","inline");$(".hide-icon-swimlane-"+a).css("display","none")};n.prototype.isCollapsed=function(a){return-1<this.getAllCollapsed().indexOf(a)};n.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]};n.prototype.refresh=function(){for(var a=this.getAllCollapsed(),b=0;b<a.length;b++)this.collapse(a[b])};n.prototype.listen=function(){var a=this;$(document).on("click",".board-swimlane-toggle",function(b){b.preventDefault();
+b=$(this).data("swimlane-id");a.isCollapsed(b)?a.expand(b):a.collapse(b)})};x.prototype.execute=function(){for(var a=$("#chart").data("metrics"),b=[],c=0;c<a.length;c++)b.push([a[c].column_title,a[c].nb_tasks]);c3.generate({data:{columns:b,type:"donut"}})};y.prototype.execute=function(){for(var a=$("#chart").data("metrics"),b=[],c=0;c<a.length;c++)b.push([a[c].user,a[c].nb_tasks]);c3.generate({data:{columns:b,type:"donut"}})};z.prototype.execute=function(){for(var a=$("#chart").data("metrics"),b=
+[],c=[],e=[],f=d3.time.format("%Y-%m-%d"),g=d3.time.format($("#chart").data("date-format")),d=0;d<a.length;d++)for(var h=0;h<a[d].length;h++)0==d?(b.push([a[d][h]]),0<h&&c.push(a[d][h])):(b[h].push(a[d][h]),0==h&&e.push(g(f.parse(a[d][h]))));c3.generate({data:{columns:b,type:"area-spline",groups:[c]},axis:{x:{type:"category",categories:e}}})};A.prototype.execute=function(){for(var a=$("#chart").data("metrics"),b=[[$("#chart").data("label-total")]],c=[],e=d3.time.format("%Y-%m-%d"),f=d3.time.format($("#chart").data("date-format")),
+g=0;g<a.length;g++)for(var d=0;d<a[g].length;d++)0==g?b.push([a[g][d]]):(b[d+1].push(a[g][d]),0<d&&(void 0==b[0][g]&&b[0].push(0),b[0][g]+=a[g][d]),0==d&&c.push(f(e.parse(a[g][d]))));c3.generate({data:{columns:b},axis:{x:{type:"category",categories:c}}})};B.prototype.execute=function(){var a=[],b=$("#chart").data("metrics"),c=$("#chart").data("labels"),e=d3.time.format("%Y-%m-%d"),f=d3.time.format($("#chart").data("date-format")),g=[[c["in"]],[c.left],[c.out]],d={};d[c["in"]]="#5858FA";d[c.left]=
+"#04B404";d[c.out]="#DF3A01";for(c=0;c<b.length;c++)a.push(f(e.parse(b[c].date))),g[0].push(b[c]["in"]),g[1].push(b[c].left),g[2].push(b[c].out);c3.generate({data:{columns:g,colors:d,type:"bar"},bar:{width:{ratio:.25}},grid:{x:{show:!0},y:{show:!0}},axis:{x:{type:"category",categories:a}}})};C.prototype.execute=function(a){var b=$("#chart").data("metrics"),c=[$("#chart").data("label")],e=[],f;for(f in b)c.push(b[f].average),e.push(b[f].title);c3.generate({data:{columns:[c],type:"bar"},bar:{width:{ratio:.5}},
+axis:{x:{type:"category",categories:e},y:{tick:{format:a.formatDuration}}},legend:{show:!1}})};D.prototype.execute=function(a){for(var b=$("#chart").data("metrics"),c=[$("#chart").data("label")],e=[],f=0;f<b.length;f++)c.push(b[f].time_spent),e.push(b[f].title);c3.generate({data:{columns:[c],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:e},y:{tick:{format:a.formatDuration}}},legend:{show:!1}})};E.prototype.execute=function(a){var b=$("#chart").data("metrics"),c=[$("#chart").data("label-cycle")],
+e=[$("#chart").data("label-lead")],f=[],g={};g[$("#chart").data("label-cycle")]="area";g[$("#chart").data("label-lead")]="area-spline";var d={};d[$("#chart").data("label-lead")]="#afb42b";d[$("#chart").data("label-cycle")]="#4e342e";for(var h=0;h<b.length;h++)c.push(parseInt(b[h].avg_cycle_time)),e.push(parseInt(b[h].avg_lead_time)),f.push(b[h].day);c3.generate({data:{columns:[e,c],types:g,colors:d},axis:{x:{type:"category",categories:f},y:{tick:{format:a.formatDuration}}}})};q.prototype.addRoute=
+function(a,b){this.routes[a]=b};q.prototype.dispatch=function(a){for(var b in this.routes)if(document.getElementById(b)){Object.create(this.routes[b].prototype).execute(a);break}};jQuery(document).ready(function(){var a=new k,b=new q;b.addRoute("board",l);b.addRoute("calendar",w);b.addRoute("screenshot-zone",m);b.addRoute("analytic-task-repartition",x);b.addRoute("analytic-user-repartition",y);b.addRoute("analytic-cfd",z);b.addRoute("analytic-burndown",A);b.addRoute("budget-chart",B);b.addRoute("analytic-avg-time-column",
+C);b.addRoute("analytic-task-time-column",D);b.addRoute("analytic-lead-cycle-time",E);b.dispatch(a)})})();
(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"&#x3C;Forrige",nextText:"Næste&#x3E;",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("da",{defaultButtonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})});(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT [Uhr]",sameElse:"L",nextDay:"[Morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[Gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"&#x3C;Zurück",nextText:"Vor&#x3E;",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"&#x3C;Ant",nextText:"Sig&#x3E;",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo<br/>el día",eventLimitText:"más"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var s="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":s=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":s=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":s=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":s=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":s=r?"vuoden":"vuotta"}return s=i(e,r)+" "+s}function i(e,t){return 10>e?t?s[e]:r[e]:e}var r="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),s=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",r[7],r[8],r[9]];(t.defineLocale||t.lang).call(t,"fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"&#xAB;Edellinen",nextText:"Seuraava&#xBB;",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fi",{defaultButtonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinalParse:/\d{1,2}(er|)/,ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var i=e;switch(n){case"s":return r||t?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(r||t?" perc":" perce");case"mm":return i+(r||t?" perc":" perce");case"h":return"egy"+(r||t?" óra":" órája");case"hh":return i+(r||t?" óra":" órája");case"d":return"egy"+(r||t?" nap":" napja");case"dd":return i+(r||t?" nap":" napja");case"M":return"egy"+(r||t?" hónap":" hónapja");case"MM":return i+(r||t?" hónap":" hónapja");case"y":return"egy"+(r||t?" év":" éve");case"yy":return i+(r||t?" év":" éve")}return""}function r(e){return(e?"":"[múlt] ")+"["+i[this.day()]+"] LT[-kor]"}var i="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(t.defineLocale||t.lang).call(t,"hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,t,n){return 12>e?n===!0?"de":"DE":n===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"előre",currentText:"ma",monthNames:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ápr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfő","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("hu",{defaultButtonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){switch(this.day()){case 0:return"[la scorsa] dddd [alle] LT";default:return"[lo scorso] dddd [alle] LT"}},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"&#x3C;Prec",nextText:"Succ&#x3E;",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("it",{defaultButtonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(e){return"+altri "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",LTS:"LTs秒",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e){return 12>e?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),e.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"&#x3C;前",nextText:"次&#x3E;",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["日","月","火","水","木","金","土"],dayNamesMin:["日","月","火","水","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("ja",{defaultButtonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),r="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");(t.defineLocale||t.lang).call(t,"nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?r[e.month()]:n[e.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("nl","nl",{closeText:"Sluiten",prevText:"←",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("nl",{defaultButtonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return 5>e%10&&e%10>1&&1!==~~(e/10)%10}function i(e,t,i){var r=e+" ";switch(i){case"m":return t?"minuta":"minutę";case"mm":return r+(n(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return r+(n(e)?"godziny":"godzin");case"MM":return r+(n(e)?"miesiące":"miesięcy");case"yy":return r+(n(e)?"lata":"lat")}}var r="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),a="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(t.defineLocale||t.lang).call(t,"pl",{months:function(e,t){return/D MMMM/.test(t)?a[e.month()]:r[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:i,y:"rok",yy:i},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"&#x3C;Poprzedni",nextText:"Następny&#x3E;",currentText:"Dziś",monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Śr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Śr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pl",{defaultButtonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinalParse:/\d{1,2}º/,ordinal:"%dº"}),e.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"&#x3C;Anterior",nextText:"Próximo&#x3E;",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt-br",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t){var n=e.split("_");return 1===t%10&&11!==t%100?n[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?n[1]:n[2]}function i(e,t,i){var a={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===i?t?"минута":"минуту":e+" "+n(a[i],+e)}function a(e,t){var n={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function r(e,t){var n={nominative:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function s(e,t){var n={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},i=/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/.test(t)?"accusative":"nominative";return n[i][e.day()]}(t.defineLocale||t.lang).call(t,"ru",{months:a,monthsShort:r,weekdays:s,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:i,mm:i,h:"час",hh:i,d:"день",dd:i,M:"месяц",MM:i,y:"год",yy:i},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e){return 4>e?"ночи":12>e?"утра":17>e?"дня":"вечера"},ordinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"&#x3C;Пред",nextText:"След&#x3E;",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ru",{defaultButtonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinalParse:/\d{1,2}(e|a)/,ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"e":1===t?"a":2===t?"a":3===t?"e":"e";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"&#xAB;Förra",nextText:"Nästa&#xBB;",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","Må","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sv",{defaultButtonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&4>=e?t[1]:t[2]},translate:function(e,t,a){var r=n.words[a];return 1===a.length?t?r[0]:r[1]:e+" "+n.correctGrammaticalCase(e,r)}};(t.defineLocale||t.lang).call(t,"sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",LTS:"LT:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:n.translate,mm:n.translate,h:n.translate,hh:n.translate,d:"dan",dd:n.translate,M:"mesec",MM:n.translate,y:"godinu",yy:n.translate},ordinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sr","sr",{closeText:"Затвори",prevText:"&#x3C;",nextText:"&#x3E;",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sr",{defaultButtonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e}})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",LTS:"LT s วินาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e){return 12>e?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),e.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"&#xAB;&#xA0;ย้อน",nextText:"ถัดไป&#xA0;&#xBB;",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("th",{defaultButtonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};(t.defineLocale||t.lang).call(t,"tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",LTS:"LT:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinalParse:/\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,ordinal:function(e){if(0===e)return e+"'ıncı";var t=e%10,a=e%100-t,r=e>=100?100:null;return e+(n[t]||n[a]||n[r])},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"&#x3C;geri",nextText:"ileri&#x3e",currentText:"bugün",monthNames:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("tr",{defaultButtonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})});(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",LTS:"Ah点m分s秒",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return 12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t?e:"下午"===t||"晚上"===t?e+12:e>=11?e:e+12},meridiem:function(e,t){var n=100*e+t;return 600>n?"凌晨":900>n?"早上":1130>n?"上午":1230>n?"中午":1800>n?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},lastWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()<e.unix()?"[上]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},sameElse:"LL"},ordinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s内",past:"%s前",s:"几秒",m:"1分钟",mm:"%d分钟",h:"1小时",hh:"%d小时",d:"1天",dd:"%d天",M:"1个月",MM:"%d个月",y:"1年",yy:"%d年"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("zh-cn","zh-CN",{closeText:"关闭",prevText:"&#x3C;上月",nextText:"下月&#x3E;",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("zh-cn",{defaultButtonText:{month:"月",week:"周",day:"日",list:"日程"},allDayText:"全天",eventLimitText:function(e){return"另外 "+e+" 个"}})}); \ No newline at end of file
diff --git a/assets/js/src/App.js b/assets/js/src/App.js
new file mode 100644
index 00000000..2c3d10a7
--- /dev/null
+++ b/assets/js/src/App.js
@@ -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">&nbsp;<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";
+};
diff --git a/assets/js/src/AvgTimeColumnChart.js b/assets/js/src/AvgTimeColumnChart.js
new file mode 100644
index 00000000..f8b7d2ee
--- /dev/null
+++ b/assets/js/src/AvgTimeColumnChart.js
@@ -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
+ }
+ });
+};
diff --git a/assets/js/src/BudgetChart.js b/assets/js/src/BudgetChart.js
new file mode 100644
index 00000000..9ab0d5a9
--- /dev/null
+++ b/assets/js/src/BudgetChart.js
@@ -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
+ }
+ }
+ });
+};
diff --git a/assets/js/src/BurndownChart.js b/assets/js/src/BurndownChart.js
new file mode 100644
index 00000000..79199b67
--- /dev/null
+++ b/assets/js/src/BurndownChart.js
@@ -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
+ }
+ }
+ });
+};
diff --git a/assets/js/src/CumulativeFlowDiagram.js b/assets/js/src/CumulativeFlowDiagram.js
new file mode 100644
index 00000000..61a0847b
--- /dev/null
+++ b/assets/js/src/CumulativeFlowDiagram.js
@@ -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
+ }
+ }
+ });
+};
diff --git a/assets/js/src/LeadCycleTimeChart.js b/assets/js/src/LeadCycleTimeChart.js
new file mode 100644
index 00000000..501ed892
--- /dev/null
+++ b/assets/js/src/LeadCycleTimeChart.js
@@ -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
+ }
+ }
+ }
+ });
+};
diff --git a/assets/js/src/Markdown.js b/assets/js/src/Markdown.js
new file mode 100644
index 00000000..3a51ffce
--- /dev/null
+++ b/assets/js/src/Markdown.js
@@ -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));
+};
diff --git a/assets/js/src/Popover.js b/assets/js/src/Popover.js
new file mode 100644
index 00000000..217ae55e
--- /dev/null
+++ b/assets/js/src/Popover.js
@@ -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(); });
+};
diff --git a/assets/js/src/Router.js b/assets/js/src/Router.js
new file mode 100644
index 00000000..d1e5de2a
--- /dev/null
+++ b/assets/js/src/Router.js
@@ -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);
+});
diff --git a/assets/js/src/Search.js b/assets/js/src/Search.js
new file mode 100644
index 00000000..d38ffd2b
--- /dev/null
+++ b/assets/js/src/Search.js
@@ -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();
+ }
+ });
+};
+
diff --git a/assets/js/src/Sidebar.js b/assets/js/src/Sidebar.js
new file mode 100644
index 00000000..0794d6b3
--- /dev/null
+++ b/assets/js/src/Sidebar.js
@@ -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);
+};
diff --git a/assets/js/src/TaskRepartitionChart.js b/assets/js/src/TaskRepartitionChart.js
new file mode 100644
index 00000000..1b1368dc
--- /dev/null
+++ b/assets/js/src/TaskRepartitionChart.js
@@ -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'
+ }
+ });
+};
diff --git a/assets/js/src/TaskTimeColumnChart.js b/assets/js/src/TaskTimeColumnChart.js
new file mode 100644
index 00000000..a95424a0
--- /dev/null
+++ b/assets/js/src/TaskTimeColumnChart.js
@@ -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
+ }
+ });
+};
diff --git a/assets/js/src/Tooltip.js b/assets/js/src/Tooltip.js
new file mode 100644
index 00000000..48102da6
--- /dev/null
+++ b/assets/js/src/Tooltip.js
@@ -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);
+ });
+}; \ No newline at end of file
diff --git a/assets/js/src/UserRepartitionChart.js b/assets/js/src/UserRepartitionChart.js
new file mode 100644
index 00000000..4255130a
--- /dev/null
+++ b/assets/js/src/UserRepartitionChart.js
@@ -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'
+ }
+ });
+};
diff --git a/assets/js/src/analytic.js b/assets/js/src/analytic.js
deleted file mode 100644
index a5df0ac6..00000000
--- a/assets/js/src/analytic.js
+++ /dev/null
@@ -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();
- }
- });
-
-})();
diff --git a/assets/js/src/base.js b/assets/js/src/base.js
deleted file mode 100644
index ffad93b8..00000000
--- a/assets/js/src/base.js
+++ /dev/null
@@ -1,394 +0,0 @@
-var Kanboard = (function() {
-
- jQuery(document).ready(function() {
- Kanboard.Init();
- });
-
- return {
-
- ShowLoadingIcon: function() {
- $("body").append('<span id="app-loading-icon">&nbsp;<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();
- }
- }
- };
-
-})();
diff --git a/assets/js/src/board.js b/assets/js/src/board.js
index 40f140bf..7015f1c6 100644
--- a/assets/js/src/board.js
+++ b/assets/js/src/board.js
@@ -1,278 +1,164 @@
-(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() {
- Kanboard.ShowLoadingIcon();
-
- $.ajax({
- 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 Board() {
+ this.app = null;
+ this.checkInterval = null;
+}
+
+Board.prototype.execute = function(app) {
+ this.app = app;
+ this.app.swimlane.refresh();
+ this.app.swimlane.listen();
+ this.poll();
+ this.keyboardShortcuts();
+ this.resizeColumnHeight();
+ this.listen();
+ this.dragAndDrop();
+ this.compactView();
+
+ $(window).resize(this.resizeColumnHeight);
+};
+
+Board.prototype.poll = function() {
+ var interval = parseInt($("#board").attr("data-check-interval"));
+
+ if (interval > 0) {
+ this.checkInterval = window.setInterval(this.check.bind(this), interval * 1000);
}
+};
- function resize_column()
- {
- var position = $(".board-swimlane").position();
- $(".board-task-list").height($(window).height() - position.top);
- }
+Board.prototype.check = function() {
+ if (this.app.isVisible()) {
- // Setup the board
- function board_load_events()
- {
- // Resize column height
- resize_column();
+ var self = this;
+ this.app.showLoadingIcon();
- // 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')
- );
+ $.ajax({
+ 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({
+ 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: this.refresh.bind(this),
+ error: this.app.hideLoadingIcon.bind(this)
+ });
+};
- // 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;
+Board.prototype.refresh = function(data) {
+ $("#board-container").replaceWith(data);
- setTimeout(function () {
- if (! $(".ui-tooltip:hover").length) {
- $(_this).tooltip("close");
- }
- }, 100);
- });
+ this.app.listen();
+ this.app.swimlane.refresh();
+ this.app.swimlane.listen();
+ this.resizeColumnHeight();
+ this.app.hideLoadingIcon();
+ this.listen();
+ this.dragAndDrop();
+ this.compactView();
+};
- // Automatic refresh
- var interval = parseInt($("#board").attr("data-check-interval"));
+Board.prototype.resizeColumnHeight = function() {
+ var position = $(".board-swimlane").position();
- if (interval > 0) {
- checkInterval = window.setInterval(board_check, interval * 1000);
- }
+ 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')
+ );
+ }
+ });
+};
- // Stop events
- function board_unload_events()
- {
- clearInterval(checkInterval);
- }
+Board.prototype.listen = function() {
+ var self = this;
- // Save and refresh the board
- function board_save(taskId, columnId, position, swimlaneId)
- {
- board_unload_events();
- Kanboard.ShowLoadingIcon();
+ $(document).on("click", ".task-board", function() {
+ window.location = $(this).data("task-url");
+ });
- $.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();
- compactview_reload();
- Kanboard.HideLoadingIcon();
- }
- });
- }
+ $(document).on('click', ".filter-toggle-scrolling", function(e) {
+ e.preventDefault();
+ self.toggleCompactView();
+ });
+};
- // 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();
- compactview_reload();
- }
- }
- });
- }
- }
+Board.prototype.toggleCompactView = function() {
+ var scrolling = localStorage.getItem("horizontal_scroll") || 1;
+ localStorage.setItem("horizontal_scroll", scrolling == 0 ? 1 : 0);
+ this.compactView();
+};
- function compactview_load_events()
- {
- jQuery(document).on('click', ".filter-toggle-scrolling", function(e) {
- e.preventDefault();
- compactview_toggle();
- });
+Board.prototype.compactView = function() {
+ if (localStorage.getItem("horizontal_scroll") == 0) {
+ $(".filter-wide").show();
+ $(".filter-compact").hide();
- compactview_reload();
+ $("#board-container").addClass("board-container-compact");
+ $("#board th").addClass("board-column-compact");
}
+ else {
+ $(".filter-wide").hide();
+ $(".filter-compact").show();
- function compactview_toggle()
- {
- var scrolling = Kanboard.GetStorageItem("horizontal_scroll") || 1;
- Kanboard.SetStorageItem("horizontal_scroll", scrolling == 0 ? 1 : 0);
- compactview_reload();
+ $("#board-container").removeClass("board-container-compact");
+ $("#board th").removeClass("board-column-compact");
}
-
- 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");
+};
+
+Board.prototype.toggleCollapsedMode = function() {
+ var self = this;
+ this.app.showLoadingIcon();
+
+ $.ajax({
+ cache: false,
+ url: $('.filter-display-mode:not([style="display: none;"]) a').attr('href'),
+ success: function(data) {
+ $('.filter-display-mode').toggle();
+ self.refresh(data);
}
- }
+ });
+};
- jQuery(document).ready(function() {
+Board.prototype.keyboardShortcuts = function() {
+ var self = this;
- if (Kanboard.Exists("board")) {
- board_load_events();
- compactview_load_events();
- keyboard_shortcuts();
+ Mousetrap.bind("c", function() { self.toggleCompactView(); });
+ Mousetrap.bind("s", function() { self.toggleCollapsedMode(); });
- $(window).resize(resize_column);
- }
+ Mousetrap.bind("n", function() {
+ self.app.popover.open($("#board").data("task-creation-url"));
});
-
-})();
+};
diff --git a/assets/js/src/calendar.js b/assets/js/src/calendar.js
index 68da57ee..ffb00dcd 100644
--- a/assets/js/src/calendar.js
+++ b/assets/js/src/calendar.js
@@ -1,51 +1,49 @@
-(function() {
+function Calendar() {
- jQuery(document).ready(function() {
- if (Kanboard.Exists("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'
- },
- eventDrop: function(event) {
- $.ajax({
- cache: false,
- url: calendar.data("save-url"),
- contentType: "application/json",
- type: "POST",
- processData: false,
- data: JSON.stringify({
- "task_id": event.id,
- "date_due": event.start.format()
- })
- });
- },
- viewRender: function() {
- var url = calendar.data("check-url");
- var params = {
- "start": calendar.fullCalendar('getView').start.format(),
- "end": calendar.fullCalendar('getView').end.format()
- };
+Calendar.prototype.execute = function() {
+ var calendar = $('#calendar');
- for (var key in params) {
- url += "&" + key + "=" + params[key];
- }
+ calendar.fullCalendar({
+ lang: $("body").data("js-lang"),
+ editable: true,
+ eventLimit: true,
+ defaultView: "month",
+ header: {
+ left: 'prev,next today',
+ center: 'title',
+ right: 'month,agendaWeek,agendaDay'
+ },
+ eventDrop: function(event) {
+ $.ajax({
+ cache: false,
+ url: calendar.data("save-url"),
+ contentType: "application/json",
+ type: "POST",
+ processData: false,
+ data: JSON.stringify({
+ "task_id": event.id,
+ "date_due": event.start.format()
+ })
+ });
+ },
+ viewRender: function() {
+ var url = calendar.data("check-url");
+ var params = {
+ "start": calendar.fullCalendar('getView').start.format(),
+ "end": calendar.fullCalendar('getView').end.format()
+ };
+
+ for (var key in params) {
+ url += "&" + key + "=" + params[key];
+ }
- $.getJSON(url, function(events) {
- calendar.fullCalendar('removeEvents');
- calendar.fullCalendar('addEventSource', events);
- calendar.fullCalendar('rerenderEvents');
- });
- }
+ $.getJSON(url, function(events) {
+ calendar.fullCalendar('removeEvents');
+ calendar.fullCalendar('addEventSource', events);
+ calendar.fullCalendar('rerenderEvents');
});
}
});
-
-})();
+};
diff --git a/assets/js/src/screenshot.js b/assets/js/src/screenshot.js
index dce49c77..fd50f8e7 100644
--- a/assets/js/src/screenshot.js
+++ b/assets/js/src/screenshot.js
@@ -1,145 +1,131 @@
-Kanboard.Screenshot = (function() {
+function Screenshot() {
+ this.pasteCatcher = null;
+}
- var pasteCatcher = null;
+Screenshot.prototype.execute = function() {
+ this.initialize();
+};
- // Setup event listener and workarounds
- function init()
- {
- destroy();
+// Setup event listener and workarounds
+Screenshot.prototype.initialize = function() {
+ this.destroy();
- if (! window.Clipboard) {
+ if (! window.Clipboard) {
- // Create a contenteditable element
- pasteCatcher = document.createElement("div");
- pasteCatcher.id = "screenshot-pastezone";
- pasteCatcher.contentEditable = "true";
+ // Create a contenteditable element
+ this.pasteCatcher = document.createElement("div");
+ this.pasteCatcher.id = "screenshot-pastezone";
+ this.pasteCatcher.contentEditable = "true";
- // Insert the content editable at the top to avoid scrolling down in the board view
- pasteCatcher.style.opacity = 0;
- pasteCatcher.style.position = "fixed";
- pasteCatcher.style.top = 0;
- pasteCatcher.style.right = 0;
- pasteCatcher.style.width = 0;
+ // Insert the content editable at the top to avoid scrolling down in the board view
+ this.pasteCatcher.style.opacity = 0;
+ this.pasteCatcher.style.position = "fixed";
+ this.pasteCatcher.style.top = 0;
+ this.pasteCatcher.style.right = 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
- pasteCatcher.focus();
+ // Set focus on the contenteditable element
+ this.pasteCatcher.focus();
- // Set the focus when clicked anywhere in the document
- document.addEventListener("click", setFocus);
+ // Set the focus when clicked anywhere in the document
+ document.addEventListener("click", this.setFocus.bind(this));
- // Set the focus when clicked in screenshot dropzone (popover)
- document.getElementById("screenshot-zone").addEventListener("click", setFocus);
- }
-
- window.addEventListener("paste", pasteHandler);
+ // Set the focus when clicked in screenshot dropzone (popover)
+ document.getElementById("screenshot-zone").addEventListener("click", this.setFocus.bind(this));
}
- // Set focus on the contentEditable element
- function setFocus()
- {
- if (pasteCatcher !== null) {
- pasteCatcher.focus();
- }
+ window.addEventListener("paste", this.pasteHandler.bind(this));
+};
+
+// Destroy contentEditable element
+Screenshot.prototype.destroy = function() {
+ if (this.pasteCatcher != null) {
+ document.body.removeChild(this.pasteCatcher);
+ }
+ else if (document.getElementById("screenshot-pastezone")) {
+ document.body.removeChild(document.getElementById("screenshot-pastezone"));
}
- // Destroy contenteditable
- function destroy()
- {
- if (pasteCatcher != null) {
- document.body.removeChild(pasteCatcher);
- }
- else if (document.getElementById("screenshot-pastezone")) {
- document.body.removeChild(document.getElementById("screenshot-pastezone"));
- }
+ document.removeEventListener("click", this.setFocus.bind(this));
+ this.pasteCatcher = null;
+};
- document.removeEventListener("click", setFocus);
- pasteCatcher = null;
+// Set focus on contentEditable element
+Screenshot.prototype.setFocus = function() {
+ if (this.pasteCatcher !== null) {
+ this.pasteCatcher.focus();
}
+};
- // Paste event callback
- function pasteHandler(e)
- {
- // Firefox doesn't have the property e.clipboardData.items (only Chrome)
- if (e.clipboardData && e.clipboardData.items) {
+// Paste event callback
+Screenshot.prototype.pasteHandler = function(e) {
+ // Firefox doesn't have the property e.clipboardData.items (only Chrome)
+ if (e.clipboardData && e.clipboardData.items) {
- var items = e.clipboardData.items;
+ var items = e.clipboardData.items;
- if (items) {
+ if (items) {
- for (var i = 0; i < items.length; i++) {
+ for (var i = 0; i < items.length; i++) {
- // Find an image in pasted elements
- if (items[i].type.indexOf("image") !== -1) {
+ // Find an image in pasted elements
+ if (items[i].type.indexOf("image") !== -1) {
- var blob = items[i].getAsFile();
+ var blob = items[i].getAsFile();
- // Get the image as base64 data
- var reader = new FileReader();
- reader.onload = function(event) {
- createImage(event.target.result);
- };
+ // Get the image as base64 data
+ var reader = new FileReader();
+ var self = this;
+ reader.onload = function(event) {
+ self.createImage(event.target.result);
+ };
- reader.readAsDataURL(blob);
- }
+ reader.readAsDataURL(blob);
}
}
}
- else {
+ }
+ else {
- // Handle Firefox
- setTimeout(checkInput, 100);
- }
+ // Handle Firefox
+ setTimeout(this.checkInput.bind(this), 100);
}
+};
- // Parse the input in the paste catcher element
- function checkInput()
- {
- var child = pasteCatcher.childNodes[0];
+// Parse the input in the paste catcher element
+Screenshot.prototype.checkInput = function() {
+ var child = this.pasteCatcher.childNodes[0];
- 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);
- }
+ if (child) {
+ // If the user pastes an image, the src attribute
+ // will represent the image as a base64 encoded string.
+ if (child.tagName === "IMG") {
+ this.createImage(child.src);
}
-
- pasteCatcher.innerHTML = "";
- }
-
- // 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);
- };
-
- var zone = document.getElementById("screenshot-zone");
- zone.innerHTML = "";
- zone.className = "screenshot-pasted";
- zone.appendChild(pastedImage);
-
- destroy();
- init();
}
- jQuery(document).ready(function() {
+ pasteCatcher.innerHTML = "";
+};
- if (Kanboard.Exists("screenshot-zone")) {
- init();
- }
- });
+// Creates a new image from a given source
+Screenshot.prototype.createImage = function(blob) {
+ var pastedImage = new Image();
+ pastedImage.src = blob;
- return {
- Init: init,
- Destroy: destroy
+ // 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);
};
-})(); \ No newline at end of file
+
+ var zone = document.getElementById("screenshot-zone");
+ zone.innerHTML = "";
+ zone.className = "screenshot-pasted";
+ zone.appendChild(pastedImage);
+
+ this.destroy();
+ this.initialize();
+};
diff --git a/assets/js/src/swimlane.js b/assets/js/src/swimlane.js
index 212d6d36..ce18dbfa 100644
--- a/assets/js/src/swimlane.js
+++ b/assets/js/src/swimlane.js
@@ -1,90 +1,67 @@
-(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');
- }
+function Swimlane() {
+}
- // 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)) || [];
+Swimlane.prototype.getStorageKey = function() {
+ return "hidden_swimlanes_" + $("#board").data("project-id");
+};
- hiddenSwimlaneIds.push(id);
+Swimlane.prototype.expand = function(swimlaneId) {
+ var swimlaneIds = this.getAllCollapsed();
+ var index = swimlaneIds.indexOf(swimlaneId);
- Kanboard.SetStorageItem(storageKey, JSON.stringify(hiddenSwimlaneIds));
+ if (index > -1) {
+ swimlaneIds.splice(index, 1);
}
- // 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);
+ localStorage.setItem(this.getStorageKey(), JSON.stringify(swimlaneIds));
- if (index > -1) {
- hiddenSwimlaneIds.splice(index, 1);
- }
+ $('.swimlane-row-' + swimlaneId).css('display', 'table-row');
+ $('.show-icon-swimlane-' + swimlaneId).css('display', 'none');
+ $('.hide-icon-swimlane-' + swimlaneId).css('display', 'inline');
+};
- Kanboard.SetStorageItem(storageKey, JSON.stringify(hiddenSwimlaneIds));
- }
+Swimlane.prototype.collapse = function(swimlaneId) {
+ var swimlaneIds = this.getAllCollapsed();
- // Check if swimlane Id is hidden. Anything > -1 means hidden.
- function isHidden(id)
- {
- return getAllHidden().indexOf(id) > -1;
+ if (swimlaneIds.indexOf(swimlaneId) < 0) {
+ swimlaneIds.push(swimlaneId);
+ localStorage.setItem(this.getStorageKey(), JSON.stringify(swimlaneIds));
}
- // Gets all swimlane Ids that are hidden
- function getAllHidden()
- {
- var storageKey = "hidden_swimlanes_" + $("#board").data("project-id");
- return JSON.parse(Kanboard.GetStorageItem(storageKey)) || [];
- }
+ $('.swimlane-row-' + swimlaneId).css('display', 'none');
+ $('.show-icon-swimlane-' + swimlaneId).css('display', 'inline');
+ $('.hide-icon-swimlane-' + swimlaneId).css('display', 'none');
+};
- // Reload the swimlane states (shown/hidden) after an ajax call
- jQuery(document).ajaxComplete(function() {
+Swimlane.prototype.isCollapsed = function(swimlaneId) {
+ return this.getAllCollapsed().indexOf(swimlaneId) > -1;
+};
- getAllHidden().map(function(swimlaneId) {
- collapse(swimlaneId);
- });
- });
+Swimlane.prototype.getAllCollapsed = function() {
+ return JSON.parse(localStorage.getItem(this.getStorageKey())) || [];
+};
- // Reload the swimlane states (shown/hidden) after page refresh
- jQuery(document).ready(function() {
+Swimlane.prototype.refresh = function() {
+ var swimlaneIds = this.getAllCollapsed();
- getAllHidden().map(function(swimlaneId) {
- collapse(swimlaneId);
- });
- });
+ for (var i = 0; i < swimlaneIds.length; i++) {
+ this.collapse(swimlaneIds[i]);
+ }
+};
- // Clicking on Show/Hide icon fires this.
- jQuery(document).on('click', ".board-swimlane-toggle", function(e) {
+Swimlane.prototype.listen = function() {
+ var self = this;
+
+ $(document).on('click', ".board-swimlane-toggle", function(e) {
e.preventDefault();
var swimlaneId = $(this).data('swimlane-id');
- if (isHidden(swimlaneId)) {
- unhide(swimlaneId);
- expand(swimlaneId);
+ if (self.isCollapsed(swimlaneId)) {
+ self.expand(swimlaneId);
}
else {
- hide(swimlaneId);
- collapse(swimlaneId);
+ self.collapse(swimlaneId);
}
});
-
-})();
+};
diff --git a/scripts/make-assets.sh b/scripts/make-assets.sh
index 0041afaa..4d708f46 100755
--- a/scripts/make-assets.sh
+++ b/scripts/make-assets.sh
@@ -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"
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"
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
+ echo "(function() { 'use strict';" > $tmp_file;
for file in $app_js; do cat "assets/js/src/${file}.js" >> $tmp_file; done
+ echo "})();" >> $tmp_file;
curl -s \
-d compilation_level=SIMPLE_OPTIMIZATIONS \