From e13872fc2e46976a668454d7528b0e7daef85d52 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Tue, 4 Aug 2015 22:51:44 -0400 Subject: Javascript refactoring --- app/Model/ProjectAnalytic.php | 6 +- app/Template/board/table_swimlane.php | 2 +- app/Template/board/task_footer.php | 16 +- app/Template/board/task_menu.php | 16 +- app/Template/board/task_private.php | 2 +- assets/js/app.js | 72 +++--- assets/js/src/App.js | 196 ++++++++++++++++ assets/js/src/AvgTimeColumnChart.js | 39 ++++ assets/js/src/BudgetChart.js | 55 +++++ assets/js/src/BurndownChart.js | 48 ++++ assets/js/src/CumulativeFlowDiagram.js | 48 ++++ assets/js/src/LeadCycleTimeChart.js | 45 ++++ assets/js/src/Markdown.js | 50 +++++ assets/js/src/Popover.js | 50 +++++ assets/js/src/Router.js | 34 +++ assets/js/src/Search.js | 61 +++++ assets/js/src/Sidebar.js | 25 +++ assets/js/src/TaskRepartitionChart.js | 18 ++ assets/js/src/TaskTimeColumnChart.js | 39 ++++ assets/js/src/Tooltip.js | 87 ++++++++ assets/js/src/UserRepartitionChart.js | 18 ++ assets/js/src/analytic.js | 355 ----------------------------- assets/js/src/base.js | 394 --------------------------------- assets/js/src/board.js | 392 ++++++++++++-------------------- assets/js/src/calendar.js | 86 ++++--- assets/js/src/screenshot.js | 204 ++++++++--------- assets/js/src/swimlane.js | 111 ++++------ scripts/make-assets.sh | 4 +- 28 files changed, 1195 insertions(+), 1278 deletions(-) create mode 100644 assets/js/src/App.js create mode 100644 assets/js/src/AvgTimeColumnChart.js create mode 100644 assets/js/src/BudgetChart.js create mode 100644 assets/js/src/BurndownChart.js create mode 100644 assets/js/src/CumulativeFlowDiagram.js create mode 100644 assets/js/src/LeadCycleTimeChart.js create mode 100644 assets/js/src/Markdown.js create mode 100644 assets/js/src/Popover.js create mode 100644 assets/js/src/Router.js create mode 100644 assets/js/src/Search.js create mode 100644 assets/js/src/Sidebar.js create mode 100644 assets/js/src/TaskRepartitionChart.js create mode 100644 assets/js/src/TaskTimeColumnChart.js create mode 100644 assets/js/src/Tooltip.js create mode 100644 assets/js/src/UserRepartitionChart.js delete mode 100644 assets/js/src/analytic.js delete mode 100644 assets/js/src/base.js 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 @@
- 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')) ?> + 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')) ?>
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') ) ?> @@ -27,31 +27,31 @@ - + - + -   +   -   +   -   +   -   +   - + 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 @@
    -
  • url->link(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Change description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Add a screenshot'), 'board', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?>
  • -
  • url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'task-board-popover') ?>
  • +
  • url->link(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Change description'), 'taskmodification', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Edit this task'), 'taskmodification', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Add a screenshot'), 'board', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
  • +
  • url->link(t('Close this task'), 'taskstatus', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?>
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') ) ?> 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 ')},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('
'+b+"
");$("#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'
'+$(this).attr("title")+ -"
"},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;$("
").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;$("
").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''}}}).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+"
");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;$("
").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'
'+$(this).attr("title")+"
";$.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''}}).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(' ')};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;bb.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-1el 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:"«Edellinen",nextText:"Seuraava»",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
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:"<Prec",nextText:"Succ>",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
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:"<前",nextText:"次>",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:"<Poprzedni",nextText:"Następny>",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:"<Anterior",nextText:"Próximo>",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:"<Пред",nextText:"След>",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:"«Förra",nextText:"Nästa»",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:"<",nextText:">",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:"« ย้อน",nextText:"ถัดไป »",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:"<geri",nextText:"ileri>",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() '); +}; + +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('
' + content + '
'); + 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; + + $("
") + .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 '
' + $(this).attr("title") + '
'; + } + + $.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 ''; + } + }) + .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(' '); - }, - - 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('
' + content + '
'); - - $("#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 '
' + $(this).attr("title") + '
'; - }, - 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; - - $("
") - .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; - - $("
") - .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 ''; - } - }).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 \ -- cgit v1.2.3