define(["ng.cla.core.lmsg","cla_angular","/intranet/projects/js/built/projects.libs.js","jquery","ng.cla.core.breadcrumb","user_picker","ng.cla.core.growl","ng.cla.core.html_editor","comments","datatable","ng.cla.core.colour_picker","ng.cla.core.date_picker"], function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_7__, __WEBPACK_EXTERNAL_MODULE_9__, __WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_11__, __WEBPACK_EXTERNAL_MODULE_12__) { return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "/intranet/projects/js/built/";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 15);
/******/ })
/************************************************************************/
/******/ ({

/***/ "../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/ng-sortable.scss":
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__("../../../node_modules/css-loader/lib/css-base.js")();
// imports


// module
exports.push([module.i, "/* ************************************** */\n/* Mandatory CSS required for ng-sortable */\n/* ************************************** */\n.as-sortable-item, .as-sortable-placeholder {\n  display: block;\n}\n\n.as-sortable-item {\n  -ms-touch-action: none;\n  touch-action: none;\n}\n\n.js-draggable .as-sortable-item-handle {\n  cursor: move;\n}\n\n.as-sortable-drag {\n  position: absolute;\n  pointer-events: none;\n  z-index: 9999;\n}\n\n.as-sortable-hidden {\n  display: none !important;\n}\n\n.as-sortable-un-selectable {\n  user-select: none;\n}\n\n/* Disables 'move' when a list item can't be moved due to permissions or screen size */\n.as-sortable-item .list-draggable-area[data-no-drag=true] {\n  cursor: default;\n}", ""]);

// exports


/***/ }),

/***/ "../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/project_details.scss":
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__("../../../node_modules/css-loader/lib/css-base.js")();
// imports


// module
exports.push([module.i, ".task-section-edit-colour {\n  display: inline-block;\n}\n\n.tasks-board {\n  overflow-x: scroll;\n  overflow-y: hidden;\n}\n\n.tasks-board-scrollable {\n  display: flex;\n  flex-flow: row nowrap;\n  width: fit-content;\n  position: relative;\n}\n\n.task-board-list {\n  padding: 10px 0;\n}\n\n.status-dot {\n  height: 18px;\n  width: 18px;\n  display: block;\n  border-radius: 10rem;\n}\n\n.status-dot-history {\n  height: 10px;\n  width: 10px;\n  display: inline-block;\n  border-radius: 10rem;\n}\n\n.board-status-column.board-status-header {\n  background-color: hsl(204, 45%, 98%);\n  padding: 10px;\n  vertical-align: middle;\n  color: hsl(218, 21%, 23%);\n}\n\n.board-status-column {\n  width: 300px;\n  display: inline-block;\n  height: 100%;\n  background-color: hsl(204, 45%, 98%);\n  margin: 0 5px;\n  min-height: 40px;\n  border-radius: 5px;\n}\n\n.board-status-media {\n  overflow: visible;\n}\n\n/* Custom styling for the board column rename form input */\n.board-status-media .board-status-input {\n  width: 120px;\n  height: 30px;\n}\n\n.btn-board-status {\n  padding-top: 3px;\n  padding-bottom: 3px;\n}\n\n/* Board mode task item styling  */\n.task-item.board-task {\n  margin: 10px;\n}\n\n.task-item.list-task {\n  padding: 0.25rem 0.5rem;\n  margin-bottom: -1px;\n  border: 1px solid hsl(214, 32%, 91%);\n}\n\n.task-item.list-task .task-item-complete {\n  text-decoration: line-through;\n}\n\n.task-item.list-task .task-item-icon {\n  font-size: 1.15rem;\n  vertical-align: middle;\n}\n\n.task-item-icon-gray {\n  color: hsl(219, 10%, 49%);\n}\n\n.task-item-text {\n  color: hsl(219, 15%, 35%);\n}\n\n.board-task {\n  position: relative;\n  margin: 10px;\n}\n\n.board-task .board-task-title a {\n  color: hsl(218, 21%, 23%);\n}\n\n.board-task-drop-container {\n  display: inline-block;\n  height: 100%;\n  width: 100%;\n}\n\n.as-sortable-dragging,\n.as-sortable-dragging .task-item.board-task {\n  background: white;\n  position: relative;\n}\n\n.as-sortable-dragging .task-item.board-task .card {\n  box-shadow: 0px 12px 8px -6px rgba(0, 0, 0, 0.15);\n}\n\n.project-header .avatar {\n  margin-right: 5px;\n  width: 30px;\n  border-radius: 15px;\n}\n\n.menu-wrapper {\n  float: right;\n  margin-left: 5px;\n  margin-top: 5px;\n}\n\n.project-header-options {\n  float: right;\n  margin-top: 8px;\n}\n\n.header-row {\n  margin-bottom: 10px;\n}\n\n.projects.details .btn-rag {\n  border: none;\n  padding: 0;\n  font-size: 15px;\n  color: hsl(219, 15%, 35%);\n  background: hsl(210, 38%, 95%);\n  height: 30px;\n  width: 30px;\n  margin-left: 5px;\n  border-radius: 10rem;\n  font-weight: bold;\n}\n\n/* Changes text colour of active RAG item to #fff */\n.projects.details .btn-rag.active {\n  color: #fff;\n}\n\n.project-description img {\n  max-width: 100%;\n  height: auto !important;\n}\n\n.sub-header {\n  text-align: right;\n}\n\n.rag-status {\n  display: inline-block;\n}\n\n.project-image {\n  border-radius: var(--theme-rounded-corners);\n  max-width: 185px;\n}\n\n@media only screen and (max-width: 768px) {\n  .sub-header {\n    text-align: left;\n  }\n}\n.project-more-info {\n  margin-top: 10px;\n}\n\n.project-update-icon {\n  box-shadow: 0 0 9px 5px rgba(0, 0, 0, 0.07);\n  border-radius: 10rem;\n  padding: 0.6rem;\n  display: block;\n}\n\n.project-summary img {\n  border-radius: 50%;\n}\n\n.project-summary small {\n  display: block;\n}\n\n.tab-content .panel {\n  margin-top: 10px;\n}\n\n.tab-content .panel h3 {\n  margin: 5px 0;\n}\n\n.project-progress {\n  background-color: hsl(210, 38%, 95%);\n  padding: 15px 15px 0;\n}\n\n.project-list-details .project-progress {\n  padding-bottom: 15px;\n}\n\n.progress {\n  margin: 0;\n  height: 6px;\n  background-color: hsl(210, 38%, 95%);\n  border-radius: 0;\n  box-shadow: none;\n}\n\n.progress-bar {\n  box-shadow: none;\n}\n\n.files-show-more {\n  margin-top: 15px;\n  padding: 5px 10px;\n  display: block;\n  border: 1px solid hsl(210, 38%, 95%);\n  text-align: center;\n  -webkit-border-radius: 4px;\n  -moz-border-radius: 4px;\n  border-radius: 4px;\n}\n\n.files-show-more:hover {\n  border-color: hsl(214, 32%, 91%);\n  background-color: hsl(204, 45%, 98%);\n  text-decoration: none;\n}\n\n/* sections */\n.new-section {\n  border-bottom: 1px solid hsl(214, 32%, 91%);\n  background-color: hsl(204, 45%, 98%);\n}\n\n/* task list */\n.task-list-section {\n  margin-bottom: 0;\n}\n\n.task-list-section .panel-heading {\n  position: relative;\n  overflow: visible;\n}\n\n.task-list-section .kolorPicker,\n.task-list-section .kolorPicker-wrapper {\n  display: inline-block;\n}\n\n.task-list-items {\n  min-height: 10px; /* Used to keep a drag+drop placeholder visible when list is empty */\n  margin-bottom: 0;\n}\n\n/* Allows board cards to be dragged between status columns when empty */\n.board-status-column [as-sortable=\"ctrl.dragTaskListeners\"] {\n  min-height: 130px;\n  height: 100%;\n}\n\n/* Aligns text in add task button to the left, overrides standard .btn-block styling */\n.btn.btn-add-task {\n  text-align: left;\n}\n\n.task-list-item .sortable {\n  cursor: move;\n}\n\n@media only screen and (max-width: 767px) {\n  .task-list-section .form-inline > input {\n    margin-bottom: 10px;\n  }\n}\n.task-item .checkbox-inline {\n  margin-top: -22px;\n}\n\n.task-list-item .section {\n  min-width: 40px;\n  min-height: 1px;\n  margin-top: -7px;\n  display: block;\n  float: right;\n}\n\n.team-role-variant-1 {\n  background-color: hsl(0, 74%, 57%);\n}\n\n.team-role-variant-2 {\n  background-color: hsl(237, 45%, 52%);\n}\n\n.team-role-variant-2 ~ .team-role-variant-2 {\n  background-color: hsl(143, 53%, 62%);\n}\n\n.team-role-variant-3 {\n  background-color: hsl(207, 71%, 57%);\n}\n\n.team-role-variant-4 {\n  background-color: hsl(179, 49%, 39%);\n}\n\n.team-role-variant-5 {\n  background-color: hsl(152, 44%, 28%);\n}\n\n.team-role-variant-6 {\n  background-color: hsl(148, 46%, 43%);\n}\n\n.team-role-variant-7 {\n  background-color: hsl(33, 88%, 65%);\n}\n\n.team-role-variant-8 {\n  background-color: hsl(0, 59%, 48%);\n}\n\n.team-role-variant-8 ~ .team-role-variant-8 {\n  background-color: hsl(242, 36%, 41%);\n}\n\n.team-role-variant-9 {\n  background-color: hsl(16, 63%, 37%);\n}\n\n.team-role-variant-10 {\n  background-color: hsl(218, 21%, 23%);\n}\n\n.task-duration-unit {\n  padding-top: 7px;\n}\n\n.task-duration-unit.hide-unit {\n  visibility: hidden;\n}\n\n/* READY FOR CONVERSION TO SCSS */\n@media only screen and (min-width: 768px) {\n  .project-info {\n    display: flex;\n  }\n}\n.project-info {\n  margin-bottom: 10px;\n}\n\n.project-info .project-info-left {\n  position: relative;\n}\n\n.project-info-left::after {\n  content: \"\";\n  width: 1px;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  background: hsl(210, 38%, 95%);\n  position: absolute;\n  margin: 0 7px;\n}\n\n@media only screen and (max-width: 768px) {\n  .project-info .project-info-left {\n    margin-bottom: 15px;\n    padding-bottom: 15px;\n  }\n  .project-info .project-info-left::after {\n    left: 0;\n    top: unset;\n    height: 1px;\n    width: auto;\n    margin: 0 15px;\n  }\n}\n/* Allows media to display overflow so custom border can be displayed on meta data */\n.meta-custom-wrapper {\n  overflow: visible;\n}\n\n.meta-custom {\n  position: relative;\n}\n\n/* Adds left-hand side border to meta blocks */\n.meta-custom::after {\n  content: \"\";\n  top: 0;\n  bottom: 10px;\n  left: 0;\n  border-left: 1px solid hsl(210, 38%, 95%);\n  position: absolute;\n  margin-left: -10px;\n}\n\n/* Hides left hand border on smaller screens */\n@media only screen and (max-width: 768px) {\n  .meta-custom::after {\n    display: none;\n  }\n}\n/* Adds custom margin to the bottom of the 'last modified' text so the 'created on' text aligns with the RAG status */\n.meta-custom-spacing {\n  margin-bottom: 18px;\n}\n\n/* Resets custom margin on smaller screens */\n@media only screen and (max-width: 768px) {\n  .meta-custom-spacing {\n    margin-bottom: 10.5px;\n  }\n}\n.loading-table {\n  opacity: 0.4;\n}\n\n.team-list-spinner {\n  left: 50%;\n  top: 50%;\n}\n\n.tasks-profile-img {\n  width: 25px;\n  height: 25px;\n}\n\n.btn.btn-custom-due-date {\n  padding-left: 0;\n}\n\n/* Custom position of completed toggle switch which aligns it right on desktop and left on smaller screens */\n.custom-toggle-position {\n  text-align: right;\n}\n\n@media only screen and (max-width: 768px) {\n  .custom-toggle-position {\n    text-align: left;\n  }\n}\n/* Adds custom styling to task priotity badge so it is vertically aligned with other items */\n.task-badge {\n  margin-right: 10px;\n  margin-top: 10px;\n  display: inline-block;\n}\n\n.delete-confirm-icon {\n  line-height: 70px;\n  font-size: 50px;\n  color: hsl(219, 10%, 49%);\n}\n\n/* Board mode - 'Saving' dialog box, which appears when a card has been moved */\n.tasks-board-saving {\n  position: fixed;\n  z-index: 99;\n  background: #fff;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  margin: 0 auto 20px;\n  padding: 10px;\n  width: 160px;\n  border-radius: 8px;\n  box-shadow: 0px 0px 18px 3px rgba(0, 0, 0, 0.1);\n}\n\n.overview-page-wrapper .card-overview {\n  box-shadow: none;\n  margin-bottom: 1rem;\n}\n\n.overview-page-wrapper .card-overview .card-header-overview {\n  padding: 0;\n}\n\n.overview-page-wrapper .card-header-overview .card-title {\n  font-weight: bold;\n}\n\n.overview-page-wrapper .card-header-overview .show-all-text {\n  font-size: 14px;\n}\n\n.overview-page-wrapper .card-header-overview .card-title .btn-overview {\n  margin-top: -20px;\n  margin-bottom: -10px;\n}\n\n.dashboard-profile-img {\n  border-radius: 10rem;\n}\n\n.overview-page-wrapper .card-body-overview {\n  max-height: 300px;\n  overflow-y: auto;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n/* Topics Styling */\n/* Topics table styling */\n.topics-table,\n.topics-table > tbody > tr > td,\n.topics-table > thead > tr > th {\n  border: none;\n}\n\n/* Topics table header styling */\n.topics-table thead tr th.sorting,\n.topics-table thead tr th.sorting_asc,\n.topics-table thead tr th.sorting_desc {\n  position: relative;\n  padding-left: 25px;\n}\n\n/* Workaround to show board tasks as like cla-card without actually being one */\n.card .card-status-border {\n  position: absolute;\n  width: 6px;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  border-radius: 8px 0 0 8px;\n  z-index: 1;\n}\n\n/* Sets minimum width of pinnable wrapper to 120px */\n.pinnable {\n  min-width: 120px;\n}\n\n/* Styling for sorting arrows */\n.sorting::after, .sorting_asc::after, .sorting_desc::after,\n.sorting::before, .sorting_asc::before, .sorting_desc::before {\n  content: \"\";\n  position: absolute;\n  left: 9px;\n  border-style: solid;\n  border-width: 2px;\n  border-color: #fff;\n  width: 7px;\n  height: 7px;\n  transform: rotate(-45deg);\n}\n\n/* Positions top arrow to the top */\n.sorting::before, .sorting_asc::before, .sorting_desc::before {\n  top: 50%;\n  margin-top: -6px;\n}\n\n/* Positions bottom arrow to the bottom */\n.sorting::after, .sorting_asc::after, .sorting_desc::after {\n  bottom: 50%;\n  margin-bottom: -6px;\n}\n\n/* Sets inactive top arrow to light grey */\n.sorting::before, .sorting_desc::before {\n  border-top-color: hsl(219, 15%, 35%);\n  border-right-color: hsl(219, 15%, 35%);\n}\n\n/* Sets inactive bottom arrow to light grey */\n.sorting::after, .sorting_asc::after {\n  border-bottom-color: hsl(219, 15%, 35%);\n  border-left-color: hsl(219, 15%, 35%);\n}\n\n/* Sets active top arrow to dark grey */\n.sorting_desc::after {\n  border-bottom-color: hsl(220, 26%, 14%);\n  border-left-color: hsl(220, 26%, 14%);\n}\n\n/* Sets active bottom arrow to dark grey */\n.sorting_asc::before {\n  border-top-color: hsl(220, 26%, 14%);\n  border-right-color: hsl(220, 26%, 14%);\n}\n\n/* Adds margin to bottom of table to fix issue with the table having to scroll */\n.topic-table-info {\n  display: block;\n  margin-top: 30px;\n  margin-bottom: 35px;\n}\n\n/* Puts the topic user images into a wrapper which stops the items inside from breaking line and limits the width of the table */\n.user-images-wrapper {\n  white-space: nowrap;\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  max-width: 425px;\n}\n\n/* Adds right hand gradient to show when there are more users than can be displayed */\n.user-images-wrapper:after {\n  content: \"\";\n  position: absolute;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  width: 40px;\n  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 100%);\n}\n\n/* Limits the width of the topic user images on tablet sized screens */\n@media only screen and (max-width: 768px) {\n  .user-images-wrapper {\n    max-width: 225px;\n  }\n}\n/* Limits the width of the topic user images on mobile sized screens */\n@media only screen and (max-width: 450px) {\n  .user-images-wrapper {\n    max-width: 85px;\n  }\n}\n/* Individual topic page styling    */\n.topic-details-list td {\n  padding: 0 10px;\n}\n\n.topic-title {\n  display: inline-block;\n}\n\n/* Project overview page */\n/* Comments component */\n.comment-attachments .comment-attachments-icon,\n.comment-count .comment-count-icon {\n  color: hsl(219, 10%, 49%);\n}\n\n/* Added a max-width to images in history so they don't overflow  */\n.history-wrapper img {\n  max-width: 100%;\n  height: auto !important;\n}\n\n/* Code to reverse styling of the files component so it fits the asthetics of the projects tabs */\n.projects-file-component .dms-view {\n  padding: 0;\n}\n\n.projects-file-component .dms-view .panel-body {\n  padding-top: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.modal-backdrop + .modal-backdrop {\n  opacity: 0;\n}\n\n.add-project-wrapper {\n  position: relative;\n}\n.add-project-wrapper + .add-project-wrapper::after {\n  content: \"\";\n  background: hsl(214, 32%, 91%);\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: -8px;\n  width: 1px;\n}", ""]);

// exports


/***/ }),

/***/ "../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/style.scss":
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__("../../../node_modules/css-loader/lib/css-base.js")();
// imports


// module
exports.push([module.i, ".task-section-edit-colour {\n  display: inline-block;\n}\n\n.tasks-board {\n  overflow-x: scroll;\n  overflow-y: hidden;\n}\n\n.tasks-board-scrollable {\n  display: flex;\n  flex-flow: row nowrap;\n  width: fit-content;\n  position: relative;\n}\n\n.task-board-list {\n  padding: 10px 0;\n}\n\n.status-dot {\n  height: 18px;\n  width: 18px;\n  display: block;\n  border-radius: 10rem;\n}\n\n.status-dot-history {\n  height: 10px;\n  width: 10px;\n  display: inline-block;\n  border-radius: 10rem;\n}\n\n.board-status-column.board-status-header {\n  background-color: hsl(204, 45%, 98%);\n  padding: 10px;\n  vertical-align: middle;\n  color: hsl(218, 21%, 23%);\n}\n\n.board-status-column {\n  width: 300px;\n  display: inline-block;\n  height: 100%;\n  background-color: hsl(204, 45%, 98%);\n  margin: 0 5px;\n  min-height: 40px;\n  border-radius: 5px;\n}\n\n.board-status-media {\n  overflow: visible;\n}\n\n/* Custom styling for the board column rename form input */\n.board-status-media .board-status-input {\n  width: 120px;\n  height: 30px;\n}\n\n.btn-board-status {\n  padding-top: 3px;\n  padding-bottom: 3px;\n}\n\n/* Board mode task item styling  */\n.task-item.board-task {\n  margin: 10px;\n}\n\n.task-item.list-task {\n  padding: 0.25rem 0.5rem;\n  margin-bottom: -1px;\n  border: 1px solid hsl(214, 32%, 91%);\n}\n\n.task-item.list-task .task-item-complete {\n  text-decoration: line-through;\n}\n\n.task-item.list-task .task-item-icon {\n  font-size: 1.15rem;\n  vertical-align: middle;\n}\n\n.task-item-icon-gray {\n  color: hsl(219, 10%, 49%);\n}\n\n.task-item-text {\n  color: hsl(219, 15%, 35%);\n}\n\n.board-task {\n  position: relative;\n  margin: 10px;\n}\n\n.board-task .board-task-title a {\n  color: hsl(218, 21%, 23%);\n}\n\n.board-task-drop-container {\n  display: inline-block;\n  height: 100%;\n  width: 100%;\n}\n\n.as-sortable-dragging,\n.as-sortable-dragging .task-item.board-task {\n  background: white;\n  position: relative;\n}\n\n.as-sortable-dragging .task-item.board-task .card {\n  box-shadow: 0px 12px 8px -6px rgba(0, 0, 0, 0.15);\n}\n\n.project-header .avatar {\n  margin-right: 5px;\n  width: 30px;\n  border-radius: 15px;\n}\n\n.menu-wrapper {\n  float: right;\n  margin-left: 5px;\n  margin-top: 5px;\n}\n\n.project-header-options {\n  float: right;\n  margin-top: 8px;\n}\n\n.header-row {\n  margin-bottom: 10px;\n}\n\n.projects.details .btn-rag {\n  border: none;\n  padding: 0;\n  font-size: 15px;\n  color: hsl(219, 15%, 35%);\n  background: hsl(210, 38%, 95%);\n  height: 30px;\n  width: 30px;\n  margin-left: 5px;\n  border-radius: 10rem;\n  font-weight: bold;\n}\n\n/* Changes text colour of active RAG item to #fff */\n.projects.details .btn-rag.active {\n  color: #fff;\n}\n\n.project-description img {\n  max-width: 100%;\n  height: auto !important;\n}\n\n.sub-header {\n  text-align: right;\n}\n\n.rag-status {\n  display: inline-block;\n}\n\n.project-image {\n  border-radius: var(--theme-rounded-corners);\n  max-width: 185px;\n}\n\n@media only screen and (max-width: 768px) {\n  .sub-header {\n    text-align: left;\n  }\n}\n.project-more-info {\n  margin-top: 10px;\n}\n\n.project-update-icon {\n  box-shadow: 0 0 9px 5px rgba(0, 0, 0, 0.07);\n  border-radius: 10rem;\n  padding: 0.6rem;\n  display: block;\n}\n\n.project-summary img {\n  border-radius: 50%;\n}\n\n.project-summary small {\n  display: block;\n}\n\n.tab-content .panel {\n  margin-top: 10px;\n}\n\n.tab-content .panel h3 {\n  margin: 5px 0;\n}\n\n.project-progress {\n  background-color: hsl(210, 38%, 95%);\n  padding: 15px 15px 0;\n}\n\n.project-list-details .project-progress {\n  padding-bottom: 15px;\n}\n\n.progress {\n  margin: 0;\n  height: 6px;\n  background-color: hsl(210, 38%, 95%);\n  border-radius: 0;\n  box-shadow: none;\n}\n\n.progress-bar {\n  box-shadow: none;\n}\n\n.files-show-more {\n  margin-top: 15px;\n  padding: 5px 10px;\n  display: block;\n  border: 1px solid hsl(210, 38%, 95%);\n  text-align: center;\n  -webkit-border-radius: 4px;\n  -moz-border-radius: 4px;\n  border-radius: 4px;\n}\n\n.files-show-more:hover {\n  border-color: hsl(214, 32%, 91%);\n  background-color: hsl(204, 45%, 98%);\n  text-decoration: none;\n}\n\n/* sections */\n.new-section {\n  border-bottom: 1px solid hsl(214, 32%, 91%);\n  background-color: hsl(204, 45%, 98%);\n}\n\n/* task list */\n.task-list-section {\n  margin-bottom: 0;\n}\n\n.task-list-section .panel-heading {\n  position: relative;\n  overflow: visible;\n}\n\n.task-list-section .kolorPicker,\n.task-list-section .kolorPicker-wrapper {\n  display: inline-block;\n}\n\n.task-list-items {\n  min-height: 10px; /* Used to keep a drag+drop placeholder visible when list is empty */\n  margin-bottom: 0;\n}\n\n/* Allows board cards to be dragged between status columns when empty */\n.board-status-column [as-sortable=\"ctrl.dragTaskListeners\"] {\n  min-height: 130px;\n  height: 100%;\n}\n\n/* Aligns text in add task button to the left, overrides standard .btn-block styling */\n.btn.btn-add-task {\n  text-align: left;\n}\n\n.task-list-item .sortable {\n  cursor: move;\n}\n\n@media only screen and (max-width: 767px) {\n  .task-list-section .form-inline > input {\n    margin-bottom: 10px;\n  }\n}\n.task-item .checkbox-inline {\n  margin-top: -22px;\n}\n\n.task-list-item .section {\n  min-width: 40px;\n  min-height: 1px;\n  margin-top: -7px;\n  display: block;\n  float: right;\n}\n\n.team-role-variant-1 {\n  background-color: hsl(0, 74%, 57%);\n}\n\n.team-role-variant-2 {\n  background-color: hsl(237, 45%, 52%);\n}\n\n.team-role-variant-2 ~ .team-role-variant-2 {\n  background-color: hsl(143, 53%, 62%);\n}\n\n.team-role-variant-3 {\n  background-color: hsl(207, 71%, 57%);\n}\n\n.team-role-variant-4 {\n  background-color: hsl(179, 49%, 39%);\n}\n\n.team-role-variant-5 {\n  background-color: hsl(152, 44%, 28%);\n}\n\n.team-role-variant-6 {\n  background-color: hsl(148, 46%, 43%);\n}\n\n.team-role-variant-7 {\n  background-color: hsl(33, 88%, 65%);\n}\n\n.team-role-variant-8 {\n  background-color: hsl(0, 59%, 48%);\n}\n\n.team-role-variant-8 ~ .team-role-variant-8 {\n  background-color: hsl(242, 36%, 41%);\n}\n\n.team-role-variant-9 {\n  background-color: hsl(16, 63%, 37%);\n}\n\n.team-role-variant-10 {\n  background-color: hsl(218, 21%, 23%);\n}\n\n.task-duration-unit {\n  padding-top: 7px;\n}\n\n.task-duration-unit.hide-unit {\n  visibility: hidden;\n}\n\n/* READY FOR CONVERSION TO SCSS */\n@media only screen and (min-width: 768px) {\n  .project-info {\n    display: flex;\n  }\n}\n.project-info {\n  margin-bottom: 10px;\n}\n\n.project-info .project-info-left {\n  position: relative;\n}\n\n.project-info-left::after {\n  content: \"\";\n  width: 1px;\n  top: 0;\n  bottom: 0;\n  right: 0;\n  background: hsl(210, 38%, 95%);\n  position: absolute;\n  margin: 0 7px;\n}\n\n@media only screen and (max-width: 768px) {\n  .project-info .project-info-left {\n    margin-bottom: 15px;\n    padding-bottom: 15px;\n  }\n  .project-info .project-info-left::after {\n    left: 0;\n    top: unset;\n    height: 1px;\n    width: auto;\n    margin: 0 15px;\n  }\n}\n/* Allows media to display overflow so custom border can be displayed on meta data */\n.meta-custom-wrapper {\n  overflow: visible;\n}\n\n.meta-custom {\n  position: relative;\n}\n\n/* Adds left-hand side border to meta blocks */\n.meta-custom::after {\n  content: \"\";\n  top: 0;\n  bottom: 10px;\n  left: 0;\n  border-left: 1px solid hsl(210, 38%, 95%);\n  position: absolute;\n  margin-left: -10px;\n}\n\n/* Hides left hand border on smaller screens */\n@media only screen and (max-width: 768px) {\n  .meta-custom::after {\n    display: none;\n  }\n}\n/* Adds custom margin to the bottom of the 'last modified' text so the 'created on' text aligns with the RAG status */\n.meta-custom-spacing {\n  margin-bottom: 18px;\n}\n\n/* Resets custom margin on smaller screens */\n@media only screen and (max-width: 768px) {\n  .meta-custom-spacing {\n    margin-bottom: 10.5px;\n  }\n}\n.loading-table {\n  opacity: 0.4;\n}\n\n.team-list-spinner {\n  left: 50%;\n  top: 50%;\n}\n\n.tasks-profile-img {\n  width: 25px;\n  height: 25px;\n}\n\n.btn.btn-custom-due-date {\n  padding-left: 0;\n}\n\n/* Custom position of completed toggle switch which aligns it right on desktop and left on smaller screens */\n.custom-toggle-position {\n  text-align: right;\n}\n\n@media only screen and (max-width: 768px) {\n  .custom-toggle-position {\n    text-align: left;\n  }\n}\n/* Adds custom styling to task priotity badge so it is vertically aligned with other items */\n.task-badge {\n  margin-right: 10px;\n  margin-top: 10px;\n  display: inline-block;\n}\n\n.delete-confirm-icon {\n  line-height: 70px;\n  font-size: 50px;\n  color: hsl(219, 10%, 49%);\n}\n\n/* Board mode - 'Saving' dialog box, which appears when a card has been moved */\n.tasks-board-saving {\n  position: fixed;\n  z-index: 99;\n  background: #fff;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  margin: 0 auto 20px;\n  padding: 10px;\n  width: 160px;\n  border-radius: 8px;\n  box-shadow: 0px 0px 18px 3px rgba(0, 0, 0, 0.1);\n}\n\n.overview-page-wrapper .card-overview {\n  box-shadow: none;\n  margin-bottom: 1rem;\n}\n\n.overview-page-wrapper .card-overview .card-header-overview {\n  padding: 0;\n}\n\n.overview-page-wrapper .card-header-overview .card-title {\n  font-weight: bold;\n}\n\n.overview-page-wrapper .card-header-overview .show-all-text {\n  font-size: 14px;\n}\n\n.overview-page-wrapper .card-header-overview .card-title .btn-overview {\n  margin-top: -20px;\n  margin-bottom: -10px;\n}\n\n.dashboard-profile-img {\n  border-radius: 10rem;\n}\n\n.overview-page-wrapper .card-body-overview {\n  max-height: 300px;\n  overflow-y: auto;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n/* Topics Styling */\n/* Topics table styling */\n.topics-table,\n.topics-table > tbody > tr > td,\n.topics-table > thead > tr > th {\n  border: none;\n}\n\n/* Topics table header styling */\n.topics-table thead tr th.sorting,\n.topics-table thead tr th.sorting_asc,\n.topics-table thead tr th.sorting_desc {\n  position: relative;\n  padding-left: 25px;\n}\n\n/* Workaround to show board tasks as like cla-card without actually being one */\n.card .card-status-border {\n  position: absolute;\n  width: 6px;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  border-radius: 8px 0 0 8px;\n  z-index: 1;\n}\n\n/* Sets minimum width of pinnable wrapper to 120px */\n.pinnable {\n  min-width: 120px;\n}\n\n/* Styling for sorting arrows */\n.sorting::after, .sorting_asc::after, .sorting_desc::after,\n.sorting::before, .sorting_asc::before, .sorting_desc::before {\n  content: \"\";\n  position: absolute;\n  left: 9px;\n  border-style: solid;\n  border-width: 2px;\n  border-color: #fff;\n  width: 7px;\n  height: 7px;\n  transform: rotate(-45deg);\n}\n\n/* Positions top arrow to the top */\n.sorting::before, .sorting_asc::before, .sorting_desc::before {\n  top: 50%;\n  margin-top: -6px;\n}\n\n/* Positions bottom arrow to the bottom */\n.sorting::after, .sorting_asc::after, .sorting_desc::after {\n  bottom: 50%;\n  margin-bottom: -6px;\n}\n\n/* Sets inactive top arrow to light grey */\n.sorting::before, .sorting_desc::before {\n  border-top-color: hsl(219, 15%, 35%);\n  border-right-color: hsl(219, 15%, 35%);\n}\n\n/* Sets inactive bottom arrow to light grey */\n.sorting::after, .sorting_asc::after {\n  border-bottom-color: hsl(219, 15%, 35%);\n  border-left-color: hsl(219, 15%, 35%);\n}\n\n/* Sets active top arrow to dark grey */\n.sorting_desc::after {\n  border-bottom-color: hsl(220, 26%, 14%);\n  border-left-color: hsl(220, 26%, 14%);\n}\n\n/* Sets active bottom arrow to dark grey */\n.sorting_asc::before {\n  border-top-color: hsl(220, 26%, 14%);\n  border-right-color: hsl(220, 26%, 14%);\n}\n\n/* Adds margin to bottom of table to fix issue with the table having to scroll */\n.topic-table-info {\n  display: block;\n  margin-top: 30px;\n  margin-bottom: 35px;\n}\n\n/* Puts the topic user images into a wrapper which stops the items inside from breaking line and limits the width of the table */\n.user-images-wrapper {\n  white-space: nowrap;\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  max-width: 425px;\n}\n\n/* Adds right hand gradient to show when there are more users than can be displayed */\n.user-images-wrapper:after {\n  content: \"\";\n  position: absolute;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  width: 40px;\n  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 100%);\n}\n\n/* Limits the width of the topic user images on tablet sized screens */\n@media only screen and (max-width: 768px) {\n  .user-images-wrapper {\n    max-width: 225px;\n  }\n}\n/* Limits the width of the topic user images on mobile sized screens */\n@media only screen and (max-width: 450px) {\n  .user-images-wrapper {\n    max-width: 85px;\n  }\n}\n/* Individual topic page styling    */\n.topic-details-list td {\n  padding: 0 10px;\n}\n\n.topic-title {\n  display: inline-block;\n}\n\n/* Project overview page */\n/* Comments component */\n.comment-attachments .comment-attachments-icon,\n.comment-count .comment-count-icon {\n  color: hsl(219, 10%, 49%);\n}\n\n/* Added a max-width to images in history so they don't overflow  */\n.history-wrapper img {\n  max-width: 100%;\n  height: auto !important;\n}\n\n/* Code to reverse styling of the files component so it fits the asthetics of the projects tabs */\n.projects-file-component .dms-view {\n  padding: 0;\n}\n\n.projects-file-component .dms-view .panel-body {\n  padding-top: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.modal-backdrop + .modal-backdrop {\n  opacity: 0;\n}\n\n.add-project-wrapper {\n  position: relative;\n}\n.add-project-wrapper + .add-project-wrapper::after {\n  content: \"\";\n  background: hsl(214, 32%, 91%);\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: -8px;\n  width: 1px;\n}\n\n.tile-my-projects-component .table-responsive {\n  border-top: none;\n}\n\n.table-avatar {\n  width: 22px;\n  position: relative;\n  margin-top: -4px;\n  border-radius: 150%;\n}\n\n.rag-R {\n  background-color: hsl(0, 74%, 57%);\n}\n\n.rag-A {\n  background-color: hsl(27, 82%, 57%);\n}\n\n.rag-G {\n  background-color: hsl(145, 44%, 51%);\n}\n\n.project-bookmark {\n  color: hsl(24, 73%, 50%);\n}\n\n.project-bookmark:hover {\n  color: lignten(hsl(24, 73%, 50%), 10%);\n}\n\n.task-item .checkbox-inline {\n  margin-top: -22px;\n}\n\n/* ng-sortable drag and drop */\n.as-sortable-placeholder {\n  min-height: 20px;\n  border: 2px dashed hsl(214, 18%, 63%);\n  border-radius: 5px;\n}\n\n/* ng-sortable drag and drop specifically for task cards*/\n.tasks-board-scrollable.tasks-board-tasks .as-sortable-placeholder {\n  width: calc(100% - 20px) !important;\n  margin: 6px 10px;\n  border-radius: 8px;\n}\n\n/* ng-sortable drag and drop for Task List*/\n.task-list-items .as-sortable-placeholder {\n  background: hsl(204, 45%, 98%);\n  margin: 5px;\n  width: calc(100% - 10px) !important;\n}\n\n.as-sortable-drag {\n  opacity: 0.8;\n  position: absolute;\n  pointer-events: none;\n  z-index: 9999;\n}\n\n.task-priority-option {\n  display: inline-block;\n  width: 16px;\n  height: 16px;\n  margin-top: 5px;\n  vertical-align: text-bottom;\n}\n\n.task-priority-sidestrip {\n  position: absolute;\n  left: 0;\n  top: 0;\n  bottom: 0;\n  width: 8px;\n  z-index: 2;\n  border-radius: 0.25rem 0 0 0.25rem;\n}\n\n.task-priority-sidestrip .task-drag-area .drag-icon {\n  position: absolute;\n  top: 50%;\n  margin-top: -8px;\n}\n\n.task-priority-1 {\n  background-color: hsl(145, 44%, 51%);\n}\n\n.task-priority-2 {\n  background-color: hsl(27, 82%, 57%);\n}\n\n.task-priority-3 {\n  background-color: hsl(0, 84%, 68%);\n}\n\n.project-component-list-item td {\n  vertical-align: middle !important;\n}\n\n.project-component-rag {\n  border-radius: 50%;\n  padding: 4px 8px;\n}\n\n.projects-component-title-bar {\n  padding: 10px;\n  margin: 0 0 10px 0;\n  overflow: auto;\n  background-color: hsl(214, 32%, 91%);\n}\n\n.projects-component-title {\n  font-size: larger;\n}\n\n.project-component-services img {\n  padding-right: 5px;\n}\n\n.alert-full-width {\n  width: 100%;\n  display: inline-block;\n}\n\n.task-drag-area {\n  height: 100%;\n}\n\n.wysibb-text {\n  overflow: auto;\n}\n\n/* Tasks Component */\n.my-tasks-component .card-header-overview {\n  display: none;\n}\n\n.tile-my-tasks-component[data-gs-width=\"1\"] .task-item .task-project-name,\n.tile-my-tasks-component[data-gs-width=\"1\"] .task-item .task-date {\n  display: block;\n  float: none !important;\n}\n\n.history_type {\n  font-weight: bold;\n}\n\n.history_list_item_title {\n  font-weight: bold;\n}\n\n.history-status {\n  font-style: italic;\n}\n\n.history-list {\n  font-style: italic;\n}\n\n.card-wrapper:nth-child(4n+1) {\n  clear: both;\n}\n\n/* MAKE RIGHTS TABLE SCROLLABLE ON SMALL DEVICES */\n.rights-table-wrapper > div {\n  overflow-y: auto;\n}\n\n/* Spacing on My Tasks component */\n.sub-items .task-project-name {\n  margin-right: 10px;\n}\n\n.collapsable-info-panel .nav-stacked > li.active > a,\n.collapsable-info-panel .nav-stacked > li.active > a:hover {\n  background: hsl(207, 71%, 57%);\n}\n\n.tasks-profile-img {\n  width: 25px;\n  height: 25px;\n}\n\n/* Tasks item styling */\n.task-users {\n  padding: 6px 12px;\n}\n\n.extra-user-count {\n  padding: 2px 5px;\n  border-radius: 10rem;\n  background: hsl(210, 38%, 95%);\n  font-weight: bold;\n}\n\n.extra-user-count:hover {\n  background: hsl(210, 38%, 100%);\n  cursor: pointer;\n}\n\n.tasks-profile-img {\n  width: 25px;\n  height: 25px;\n}\n\n.cursor-default {\n  cursor: default;\n}\n\n/* Styles individual task profile images */\n.tasks-profile-single {\n  margin-left: 3px;\n  margin-bottom: 3px;\n  display: inline-block;\n}\n\n/* Adds a seperator line between the Polls and Surveys in Add modal. */\n.add-tasks-wrapper {\n  position: relative;\n}\n\n.add-tasks-wrapper + .add-tasks-wrapper::after {\n  content: \"\";\n  background: red;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: -8px;\n  width: 1px;\n}\n\n.projects-file-component .document-list .icons-view .document-item {\n  flex: 0 0 25%;\n  max-width: 25%;\n}\n\n.drag-container {\n  height: 100%;\n  position: relative;\n}", ""]);

// exports


/***/ }),

/***/ "../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./js/ng/modules/widgets/css/user_list.component.scss":
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__("../../../node_modules/css-loader/lib/css-base.js")();
// imports


// module
exports.push([module.i, ".user-list-component .user-list-wrapper {\n  white-space: nowrap;\n}\n.user-list-component .user-list-wrapper::after {\n  content: \"\";\n  clear: both;\n  height: 0;\n  display: block;\n}\n.user-list-component .user-list, .user-list-component .plus-number-wrapper {\n  margin: 0;\n  padding: 0;\n}\n.user-list-component .user-list {\n  margin-right: 3%;\n  display: inline;\n}\n.user-list-component .plus-number-wrapper {\n  width: 18%;\n  position: relative;\n  background: hsl(214, 32%, 91%); /* $gray-300 */\n  border-radius: 350px;\n  display: inline-block;\n  vertical-align: top;\n  cursor: pointer;\n}\n.user-list-component .plus-number-wrapper::after {\n  content: \"\";\n  display: block;\n  padding-bottom: 100%;\n}\n.user-list-component .plus-number-wrapper .plus-number {\n  position: absolute;\n  font-size: 14px;\n  top: 50%;\n  left: 0;\n  right: 0;\n  text-align: center;\n  transform: translate(0, -50%);\n  line-height: 0;\n}\n.user-list-component .plus-number-wrapper .plus-number:hover {\n  text-decoration: none;\n  color: var(--black);\n  opacity: 0.8;\n}\n.user-list-component .plus-number-wrapper .plus-number.plus-number[href=\"javascript:void(0);\"]:hover {\n  opacity: 1;\n  cursor: default;\n}\n.user-list-component .title {\n  font-size: 14px;\n}\n.user-list-component .title, .user-list-component .plus-number {\n  color: var(--black);\n}\n.user-list-component div.user-list-item {\n  float: left;\n  list-style: none;\n  width: 18%;\n  margin-right: 2%;\n}\n.user-list-component div.user-list-item a {\n  overflow: hidden;\n  border-radius: 350px;\n  display: block;\n}\n.user-list-component div.user-list-item a[href=\"javascript:void(0);\"]:hover {\n  opacity: 1;\n  cursor: default;\n}\n.user-list-component div.user-list-item a img {\n  width: 100%;\n}", ""]);

// exports


/***/ }),

/***/ "../../../node_modules/css-loader/index.js!../js/kolorpicker/style/kolorpicker.css":
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__("../../../node_modules/css-loader/lib/css-base.js")();
// imports


// module
exports.push([module.i, "\n/* Color picker input styling */\n.kolorPicker {\n    position: relative;\n    cursor: pointer;\n    max-width: 98px;\n    border-radius: var(--theme-rounded-corners);\n}\n\n/* Color picker dropdown container */\n.color-picker-container {\n    position: absolute;\n    background: #fff;\n    padding: 1.2rem;\n    width: 350px;\n    z-index: 99;\n    margin-top: 1px;\n    cursor: pointer;\n    border-radius: .5rem;\n    box-shadow: 0px 0px 8px 8px rgba(0,0,0,.03);\n}\n\n\n/* Color picker dropdown header */\n.color-picker-header {\n    display: flex;\n    align-items: flex-start;\n    justify-content: space-between;\n    margin: 0 0 .5rem;\n}\n\n/* Color picker dropdown header close button */\n.color-picker-close-button {\n    font-size: 1.5rem;\n}\n\n/* Color picker palette nav wrapper */\n.color-picker-nav-wrapper {\n    margin-bottom: .6rem;\n}\n\n/* Color picker palette nav list */\n.color-picker-nav {\n    margin: 0;\n    padding-left: 0;\n    background: #edf2f7;\n    overflow: hidden;\n    padding: .25rem;\n    border-radius: .25rem;\n}\n\n/* Color picker palette nav item */\n.color-picker-nav .color-picker-nav-item {\n    list-style: none;\n    float: left;\n    text-align: center;\n    font-size: 14px;\n    width: 50%;\n    padding: .2rem;\n    color: #353448;\n    cursor: pointer;\n    border-radius: .25rem;\n}\n\n/* Color picker palette nav item when hovered */\n.color-picker-nav .color-picker-nav-item:hover {\n    opacity: .8;\n    transition: .3s;\n}\n\n/* Color picker palette nav item styling when active */\n.color-picker-nav .color-picker-nav-item.color-picker-nav-item-active,\n.color-picker-nav .color-picker-nav-item.color-picker-nav-item-active:hover  {\n    background: #4399df;\n    color: #fff;\n    box-shadow: 0 0 10px 0 rgba(0,0,0,.17);\n}\n\n/* Adds border and spacing below black and white swatches */\n.swatch-row-0 {\n    border-bottom: 1px solid #eaeef0;\n    padding-bottom: .6rem;\n    margin-bottom: .2rem;\n}\n\n\n/* Responsive styling of color swatches */\n.swatch {\n    float: left;\n    margin-top: 2.22%;\n    margin-right: 2.22%;\n    width: 8%;\n    border-radius: 10rem;\n    cursor: pointer;\n    position: relative;\n}\n\n/* Allows color swatches to keep their aspect ratio at any screen size */\n.swatch:after {\n    content: \"\";\n    display: block;\n    padding-bottom: 100%;\n}\n\n.swatch:hover {\n    opacity: .8;\n}\n\n/* Adds border and shadow to the current picked swatch */\n.swatch.current-swatch::before {\n    content: '';\n    border: 1px solid #80bdff;\n    box-shadow: 0 0 0 2px rgba(0, 123, 255, .25);\n    position: absolute;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    left: 0;\n    margin: -3px;\n    border-radius: 10rem;\n}\n\n\n/* Adds a gray border to lighter colored swatches so they're more visible */\n#ffffff, #f7fafc, #fff5f5, #fffaf0, #f0fff4,\n#e6fffa, #ebf8ff, #ebf4ff,#faf5ff, #fff5f7, #fffff0{\n    border: 1px solid #ccd5df;\n}\n\n/* Hides colors which are too light to be used in certain areas. */\n\n.js-no-white #ffffff,\n/* Lightest line */\n.js-no-white #f7fafc, .js-no-white #fff5f5, .js-no-white #fffaf0, .js-no-white #f0fff4, .js-no-white #e6fffa,\n.js-no-white #ebf8ff, .js-no-white #ebf4ff, .js-no-white #faf5ff, .js-no-white #fff5f7, .js-no-white #fffff0,\n/* Second lightest line */\n.js-no-white #edf2f7, .js-no-white #fed7d7, .js-no-white #fdebc9, .js-no-white #fdfbc0, .js-no-white #c7f5d5,\n.js-no-white #b3f4e9, .js-no-white #bfe3f7, .js-no-white #c4dafd, .js-no-white #e9d8fd, .js-no-white #fed7e2{\n    display: none;\n}\n\n\n/* Removes margin from the 10th swatch in each row in the color picker container so they're perfectly centred in the wrapper  */\n.color-picker-container .swatch:nth-of-type(10n+2) {\n    margin-right: 0;\n}\n\n\n/* Color picker sizing and positioning on mobile */\n@media only screen and (max-width: 768px) {\n    .color-picker-wrapper {\n        right: 20px;\n        width: 82%;\n    }\n}\n\n@media only screen and (max-width: 576px) {\n    .color-picker-container {\n        width: 100%;\n    }\n}\n\n/* Color picker helper code */\n.kolorPicker[data-helper=\"true\"] {\n    max-width: 120px;\n}\n\n.kolorPicker[data-swatches=\"true\"] {\n    max-width: 289px;\n}\n\n.kolorPicker.kp-has-helper {\n    padding-bottom: 22px;\n    height: 55px;\n}\n\n.kp-helper {\n    cursor: pointer;\n    font-weight: 300;\n    position: absolute;\n    z-index: 2;\n    color: #000;\n    font-size: 11px;\n    margin-top: -24px;\n    margin-left: 14px;\n}\n\n.kolorPicker-wrapper + .kp-helper {\n    z-index: 320;\n}\n\n.open-swatches {\nmax-width: 320px;\n}\n", ""]);

// exports


/***/ }),

/***/ "../../../node_modules/css-loader/lib/css-base.js":
/***/ (function(module, exports) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function() {
	var list = [];

	// return the list of modules as css string
	list.toString = function toString() {
		var result = [];
		for(var i = 0; i < this.length; i++) {
			var item = this[i];
			if(item[2]) {
				result.push("@media " + item[2] + "{" + item[1] + "}");
			} else {
				result.push(item[1]);
			}
		}
		return result.join("");
	};

	// import a list of modules into the list
	list.i = function(modules, mediaQuery) {
		if(typeof modules === "string")
			modules = [[null, modules, ""]];
		var alreadyImportedModules = {};
		for(var i = 0; i < this.length; i++) {
			var id = this[i][0];
			if(typeof id === "number")
				alreadyImportedModules[id] = true;
		}
		for(i = 0; i < modules.length; i++) {
			var item = modules[i];
			// skip already imported module
			// this implementation is not 100% perfect for weird media query combinations
			//  when a module is imported multiple times with different media queries.
			//  I hope this will never occur (Hey this way we have smaller bundles)
			if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
				if(mediaQuery && !item[2]) {
					item[2] = mediaQuery;
				} else if(mediaQuery) {
					item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
				}
				list.push(item);
			}
		}
	};
	return list;
};


/***/ }),

/***/ "../../../node_modules/style-loader/addStyles.js":
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var stylesInDom = {},
	memoize = function(fn) {
		var memo;
		return function () {
			if (typeof memo === "undefined") memo = fn.apply(this, arguments);
			return memo;
		};
	},
	isOldIE = memoize(function() {
		// Test for IE <= 9 as proposed by Browserhacks
		// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
		// Tests for existence of standard globals is to allow style-loader 
		// to operate correctly into non-standard environments
		// @see https://github.com/webpack-contrib/style-loader/issues/177
		return window && document && document.all && !window.atob;
	}),
	getElement = (function(fn) {
		var memo = {};
		return function(selector) {
			if (typeof memo[selector] === "undefined") {
				memo[selector] = fn.call(this, selector);
			}
			return memo[selector]
		};
	})(function (styleTarget) {
		return document.querySelector(styleTarget)
	}),
	singletonElement = null,
	singletonCounter = 0,
	styleElementsInsertedAtTop = [],
	fixUrls = __webpack_require__("../../../node_modules/style-loader/fixUrls.js");

module.exports = function(list, options) {
	if(typeof DEBUG !== "undefined" && DEBUG) {
		if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};
	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (typeof options.singleton === "undefined") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
	if (typeof options.insertInto === "undefined") options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (typeof options.insertAt === "undefined") options.insertAt = "bottom";

	var styles = listToStyles(list);
	addStylesToDom(styles, options);

	return function update(newList) {
		var mayRemove = [];
		for(var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];
			domStyle.refs--;
			mayRemove.push(domStyle);
		}
		if(newList) {
			var newStyles = listToStyles(newList);
			addStylesToDom(newStyles, options);
		}
		for(var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];
			if(domStyle.refs === 0) {
				for(var j = 0; j < domStyle.parts.length; j++)
					domStyle.parts[j]();
				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom(styles, options) {
	for(var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];
		if(domStyle) {
			domStyle.refs++;
			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}
			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];
			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}
			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles(list) {
	var styles = [];
	var newStyles = {};
	for(var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};
		if(!newStyles[id])
			styles.push(newStyles[id] = {id: id, parts: [part]});
		else
			newStyles[id].parts.push(part);
	}
	return styles;
}

function insertStyleElement(options, styleElement) {
	var styleTarget = getElement(options.insertInto)
	if (!styleTarget) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}
	var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
	if (options.insertAt === "top") {
		if(!lastStyleElementInsertedAtTop) {
			styleTarget.insertBefore(styleElement, styleTarget.firstChild);
		} else if(lastStyleElementInsertedAtTop.nextSibling) {
			styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			styleTarget.appendChild(styleElement);
		}
		styleElementsInsertedAtTop.push(styleElement);
	} else if (options.insertAt === "bottom") {
		styleTarget.appendChild(styleElement);
	} else {
		throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
	}
}

function removeStyleElement(styleElement) {
	styleElement.parentNode.removeChild(styleElement);
	var idx = styleElementsInsertedAtTop.indexOf(styleElement);
	if(idx >= 0) {
		styleElementsInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement(options) {
	var styleElement = document.createElement("style");
	options.attrs.type = "text/css";

	attachTagAttrs(styleElement, options.attrs);
	insertStyleElement(options, styleElement);
	return styleElement;
}

function createLinkElement(options) {
	var linkElement = document.createElement("link");
	options.attrs.type = "text/css";
	options.attrs.rel = "stylesheet";

	attachTagAttrs(linkElement, options.attrs);
	insertStyleElement(options, linkElement);
	return linkElement;
}

function attachTagAttrs(element, attrs) {
	Object.keys(attrs).forEach(function (key) {
		element.setAttribute(key, attrs[key]);
	});
}

function addStyle(obj, options) {
	var styleElement, update, remove;

	if (options.singleton) {
		var styleIndex = singletonCounter++;
		styleElement = singletonElement || (singletonElement = createStyleElement(options));
		update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
		remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
	} else if(obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function") {
		styleElement = createLinkElement(options);
		update = updateLink.bind(null, styleElement, options);
		remove = function() {
			removeStyleElement(styleElement);
			if(styleElement.href)
				URL.revokeObjectURL(styleElement.href);
		};
	} else {
		styleElement = createStyleElement(options);
		update = applyToTag.bind(null, styleElement);
		remove = function() {
			removeStyleElement(styleElement);
		};
	}

	update(obj);

	return function updateStyle(newObj) {
		if(newObj) {
			if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
				return;
			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;
		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag(styleElement, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (styleElement.styleSheet) {
		styleElement.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = styleElement.childNodes;
		if (childNodes[index]) styleElement.removeChild(childNodes[index]);
		if (childNodes.length) {
			styleElement.insertBefore(cssNode, childNodes[index]);
		} else {
			styleElement.appendChild(cssNode);
		}
	}
}

function applyToTag(styleElement, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		styleElement.setAttribute("media", media)
	}

	if(styleElement.styleSheet) {
		styleElement.styleSheet.cssText = css;
	} else {
		while(styleElement.firstChild) {
			styleElement.removeChild(styleElement.firstChild);
		}
		styleElement.appendChild(document.createTextNode(css));
	}
}

function updateLink(linkElement, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/* If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
	and there is no publicPath defined then lets turn convertToAbsoluteUrls
	on by default.  Otherwise default to the convertToAbsoluteUrls option
	directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls){
		css = fixUrls(css);
	}

	if(sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = linkElement.href;

	linkElement.href = URL.createObjectURL(blob);

	if(oldSrc)
		URL.revokeObjectURL(oldSrc);
}


/***/ }),

/***/ "../../../node_modules/style-loader/fixUrls.js":
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),

/***/ "../js/kolorpicker/style/kolorpicker.css":
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__("../../../node_modules/css-loader/index.js!../js/kolorpicker/style/kolorpicker.css");
if(typeof content === 'string') content = [[module.i, content, '']];
// add the styles to the DOM
var update = __webpack_require__("../../../node_modules/style-loader/addStyles.js")(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../../../../node_modules/css-loader/index.js!./kolorpicker.css", function() {
			var newContent = require("!!../../../../../node_modules/css-loader/index.js!./kolorpicker.css");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ "./assets/css/ng-sortable.scss":
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__("../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/ng-sortable.scss");
if(typeof content === 'string') content = [[module.i, content, '']];
// add the styles to the DOM
var update = __webpack_require__("../../../node_modules/style-loader/addStyles.js")(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./ng-sortable.scss", function() {
			var newContent = require("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./ng-sortable.scss");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ "./assets/css/project_details.scss":
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__("../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/project_details.scss");
if(typeof content === 'string') content = [[module.i, content, '']];
// add the styles to the DOM
var update = __webpack_require__("../../../node_modules/style-loader/addStyles.js")(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./project_details.scss", function() {
			var newContent = require("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./project_details.scss");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ "./assets/css/style.scss":
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__("../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./assets/css/style.scss");
if(typeof content === 'string') content = [[module.i, content, '']];
// add the styles to the DOM
var update = __webpack_require__("../../../node_modules/style-loader/addStyles.js")(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./style.scss", function() {
			var newContent = require("!!../../../../../node_modules/css-loader/index.js!../../../../../node_modules/sass-loader/lib/loader.js!./style.scss");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ "./html/directives/history_changes.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_changes.directive.html';
var html = "<div ng-repeat=\"change in ctrl.changes.changes\">\n\t<span class=\"history_list_item_title\">{{change.type_title}}</span>:\n\t<span ng-if=\"change.old !== undefined\">\n\t\t<span ng-if=\"!ctrl.isSpecialUpdateType(change.type)\">{{change.old}} => {{change.new}}</span>\n\t\t<span ng-if=\"change.type === 'assignee'\"><history-user user=\"change.old\"></history-user> => <history-user user=\"change.new\"></history-user></span>\n\t\t<span ng-if=\"change.type === 'assignees'\"><span ng-repeat=\"user in change.old\"><history-user user=\"user\">{{user}}</history-user><span ng-if=\"!$last\">, </span></span> => <span ng-repeat=\"user in change.new\"><history-user user=\"user\"></history-user><span ng-if=\"!$last\">, </span></span></span>\n\t\t<span ng-if=\"change.type === 'priority'\"><history-priority priority=\"{{change.old}}\"></history-priority> => <history-priority priority=\"{{change.new}}\"></history-priority></span>\n\t\t<span ng-if=\"change.type === 'status'\"><history-status status=\"change.old\" status-str=\"change.old_str\"></history-status> => <history-status status=\"change.new\" status-str=\"change.new_str\"></history-status></span>\n\t\t<span ng-if=\"change.type === 'list'\"><history-list list=\"change.old\" list-str=\"change.old_str\"></history-list> => <history-list list=\"change.new\" list-str=\"change.new_str\"></history-list></span>\n\t\t<span ng-if=\"change.type === 'is_private'\"><history-is-private private=\"change.old\"></history-is-private> => <history-is-private private=\"change.new\"></history-is-private></span>\n\t</span>\n\t<span ng-if=\"change.old === undefined\">\n\t\t<span ng-if=\"!ctrl.isSpecialUpdateType(change.type)\" ng-bind=\"change.value\"></span>\n\t\t<span ng-if=\"change.type === 'assignee'\"><history-user user=\"change.value\"></history-user></span>\n\t\t<span ng-if=\"change.type === 'assignees'\"><span ng-repeat=\"user in change.value\"><history-user user=\"user\">{{user}}</history-user><span ng-if=\"!$last\">, </span></span></span>\n\t\t<span ng-if=\"change.type === 'priority'\"><history-priority priority=\"{{change.value}}\"></history-priority></span>\n\t\t<span ng-if=\"change.type === 'status'\"><history-status status=\"change.value\" status-str=\"change.value_str\"></history-status></span>\n\t\t<span ng-if=\"change.type === 'list'\"><history-list list=\"change.value\" list-str=\"change.value_str\"></history-list></span>\n\t\t<span ng-if=\"change.type === 'is_private'\"><history-is-private private=\"change.value\"></history-is-private></span>\n\t</span>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/history_is_private.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_is_private.directive.html';
var html = "<span>{{ ctrl.getTaskPrivateName() }}</span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/history_list.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_list.directive.html';
var html = "<span class=\"history-list\" ng-bind=\"ctrl.getListName()\"></span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/history_priority.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_priority.directive.html';
var html = "<span ng-if=\"ctrl.priority > 0\" class=\"task-priority-option task-priority-{{ctrl.priority}}\">&nbsp</span> {{ctrl.getPriorityName()}}\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/history_status.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_status.directive.html';
var html = "<span class=\"history-status\" ng-bind=\"ctrl.getStatusName()\"></span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/history_user.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/history_user.directive.html';
var html = "<a ng-if=\"ctrl.isUser()\" ng-href=\"{{ctrl.user.profile_url}}\">{{ctrl.user.name}}</a>\n<span ng-if=\"!ctrl.isUser()\" lmsg=\"projects.task.unassigned\">[Unassigned]</span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/project_nav_tabs.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/project_nav_tabs.directive.html';
var html = "<!-- Nav tabs -->\n<ul class=\"nav flex-column p-3\" role=\"tablist\" id=\"nav-tabs\" ng-cloak=\"\">\n\t<li class=\"nav-item\" role=\"presentation\" ng-show=\"ctrl.project_perms.can_view_tasks\" ng-class=\"{active: ctrl.tab==='overviewTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/overviewTab\" aria-controls=\"overviewTab\">\n\t\t\t<txt lmsg=\"projects.project.overview\">[Project Overview]</txt>\n\t\t</a>\n\t</li>\n\t<li class=\"nav-item\" role=\"presentation\" ng-show=\"ctrl.project_perms.can_view_tasks\" ng-class=\"{active: ctrl.tab==='tasksTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/tasksTab\" aria-controls=\"taskTab\">\n\t\t\t<txt lmsg=\"projects.project.tasks\">[Tasks]</txt>\n\t\t\t<span class=\"badge badge-light float-right\" ng-if=\"ctrl.tabCounts.taskCount > 0\">{{ctrl.tabCounts.taskCount}}<span ng-if=\"ctrl.tabCounts.totalTaskCount > 0\"> of {{ctrl.tabCounts.totalTaskCount}}</span></span>\n\t\t</a>\n\t</li>\n\t<li class=\"nav-item\" role=\"presentation\" name=\"teamTab\" ng-show=\"ctrl.project_perms.can_view_team\" ng-class=\"{active: ctrl.tab==='teamTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/teamTab\" aria-controls=\"teamTab\">\n\t\t\t<txt lmsg=\"projects.project.team\">[Team]</txt>\n\t\t\t<span class=\"badge badge-light float-right\" ng-if=\"ctrl.tabCounts.teamCount > 0\">{{ctrl.tabCounts.teamCount}}</span>\n\t\t</a>\n\t</li>\n\t<li class=\"nav-item\" role=\"presentation\" ng-show=\"ctrl.project_perms.can_view_discussion\" ng-class=\"{active: ctrl.tab==='discussionTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/discussionTab\" aria-controls=\"discussionTab\"><txt lmsg=\"projects.project.topics\">[Topics]</txt></a>\n\t</li>\n\t<li class=\"nav-item\" role=\"presentation\" ng-show=\"ctrl.project_perms.can_view_files || ctrl.project_perms.can_manage_files\" ng-class=\"{active: ctrl.tab==='filesTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/filesTab\" aria-controls=\"filesTab\">\n\t\t\t<txt lmsg=\"projects.project.files\">[Files]</txt>\n\t\t\t<span class=\"badge badge-light float-right\" ng-if=\"ctrl.tabCounts.fileCount > 0\">{{ctrl.tabCounts.fileCount}}</span>\n\t\t</a>\n\t</li>\n\t<li class=\"nav-item\" role=\"presentation\" ng-show=\"ctrl.project_perms.can_view_tasks\" ng-class=\"{active: ctrl.tab==='historyTab'}\">\n\t\t<a class=\"nav-link\" href=\"#/historyTab\" aria-controls=\"historyTab\">\n\t\t\t<txt lmsg=\"projects.project.history\">[History]</txt>\n\t\t</a>\n\t</li>\n</ul>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/task.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/task.directive.html';
var html = "<!--- START View Task -->\n<div ng-if=\"item.display !== 'board'\">\n\t<div class=\"task-item list-task\">\n\t\t<div as-sortable-item-handle=\"\">\n\t\t\t<!-- start bootstrap grid -->\n\t\t\t<div class=\"list-draggable-area\" ng-attr-data-no-drag=\"{{item.noDrag()}}\">\n\t\t\t\t<div class=\"media\">\n\t\t\t\t\t<div class=\"mr-1\">\n\t\t\t\t\t\t<clang-checkbox name=\"checkbox_{{item.task.id}}\" large=\"1\" ng-model=\"item.task.is_complete\" ng-change=\"item.completeToggled($event)\" ng-show=\"item.task.perms.can_update\"></clang-checkbox>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"media-body\">\n\t\t\t\t\t\t<div class=\"row\">\n\t\t\t\t\t\t\t<div class=\"col-12\">\n\t\t\t\t\t\t\t\t<div class=\"media\">\n\t\t\t\t\t\t\t\t\t<div class=\"media-body\">\n\t\t\t\t\t\t\t\t\t\t<span ng-class=\"{'task-item-complete': item.task.is_complete}\">\n\t\t\t\t\t\t\t\t\t\t\t<a ng-if=\"item.task.title !== item.task.title_truncated\" ng-href=\"/projects/task/{{item.task.id}}\" rel=\"tooltip\" data-original-title=\"{{item.task.title}}\">{{item.task.title_truncated}}</a>\n\t\t\t\t\t\t\t\t\t\t\t<a ng-if=\"item.task.title === item.task.title_truncated\" ng-href=\"/projects/task/{{item.task.id}}\">{{item.task.title}}</a>\n\t\t\t\t\t\t\t\t\t\t\t<a ng-href=\"{{item.task.source.link}}\" ng-show=\"(item.showSource) && (item.task.source) && (item.task.source.name !== '')\"><span class=\"text-muted\">({{item.task.source.name}})</span></a>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t<div class=\"ml-2\">\n\t\t\t\t\t\t\t\t\t\t<!--- edit -->\n\t\t\t\t\t\t\t\t\t\t<span ng-if=\"item.showEdit\" >\n\t\t\t\t\t\t\t\t\t\t\t<button class=\"js-task_edit_button_{{item.task.id}} btn btn-link btn-sm\" rel=\"tooltip\" lmsg=\"title:common.edit\" name=\"projects:task:edit\" ng-click=\"item.startTaskEdit($event, item.task.id)\" ng-if=\"item.task.perms.can_edit\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ion-icon name=\"create-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\n\t\t\t\t\t\t<div class=\"row\">\n\t\t\t\t\t\t\t<div class=\"col-12 align-self-center\">\n\t\t\t\t\t\t\t\t<!--- due date -->\n\t\t\t\t\t\t\t\t<span ng-if=\"item.showDate\">\n\t\t\t\t\t\t\t\t\t<span class=\"py-1 pl-0 pr-2 pl-0 mr-2 float-left align-middle\" ng-if=\"item.task.due_date_formatted != ''\">\n\t\t\t\t\t\t\t\t\t\t<span ng-class=\"{'text-danger': item.task.due_date_type=='overdue', 'text-info': item.task.due_date_type=='today'}\">\n\t\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon task-item-icon-gray\" name=\"calendar-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"align-middle small\" ng-if=\"item.task.due_date_type!='today' && item.task.due_date_type!='tomorrow'\">{{item.task.due_date_formatted}}</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"align-middle small\" ng-if=\"item.task.due_date_type=='today'\" rel=\"tooltip\" data-original-title=\"{{item.task.due_date_formatted}}\">Today</span>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"align-middle small task-item-text\" ng-if=\"item.task.due_date_type=='tomorrow'\" rel=\"tooltip\" data-original-title=\"{{item.task.due_date_formatted}}\">Tomorrow</span>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!--- Status indicator -->\n\t\t\t\t\t\t\t\t<span ng-if=\"item.task.priority !== 0\" ng-switch=\"item.task.priority\" class=\"py-1 pl-0 pr-2 pl-0 mr-2 float-left align-middle\">\n\t\t\t\t\t\t\t\t\t<span class=\"priority-low text-success\" ng-switch-when=\"1\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon\" name=\"arrow-down-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"priority-medium text-warning\" ng-switch-when=\"2\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon\" name=\"arrow-up-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"priority-high text-danger\" ng-switch-when=\"3\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon\" name=\"arrow-up-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!--- private note -->\n\t\t\t\t\t\t\t\t<span ng-if=\"item.showPrivate\" class=\"float-left align-middle\">\n\t\t\t\t\t\t\t\t\t<span class=\"py-1 pl-0 pr-2 pl-0 mr-2 float-left\" ng-if=\"item.task.is_private\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon task-item-icon-gray\" name=\"lock-closed-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!--- comments -->\n\t\t\t\t\t\t\t\t<span class=\"float-left align-middle\">\n\t\t\t\t\t\t\t\t\t<a href=\"/projects/task/{{item.task.id}}\" class=\"py-1 pl-0 pr-2 pl-0 mr-2 float-left text-reset\" ng-if=\"item.task.comments_count>0\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon task-item-icon-gray\" name=\"chatbox-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t\t<span class=\"small task-item-text\">{{item.task.comments_count}}</span>\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!--- files -->\n\t\t\t\t\t\t\t\t<span ng-if=\"item.showFiles\" class=\"float-left align-middle\">\n\t\t\t\t\t\t\t\t\t<a href=\"/projects/task/{{item.task.id}}#/filesTab\" ng-if=\"item.task.has_file_storage\" class=\"py-1 pl-0 pr-2 pl-0 mr-2 d-inline-block text-reset\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon class=\"task-item-icon task-item-icon-gray\" name=\"attach-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!-- Status -->\n\t\t\t\t\t\t\t\t<span class=\"float-left align-middle py-1 pl-0 pr-2 pl-0 mr-2\">\n\t\t\t\t\t\t\t\t\t<cla-badge styleclass=\"badge badge-pill badge-secondary\">{{(item.task.status_list | filter : {id: item.task.status_id})[0].name }}</cla-badge>\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<!--- assigned to -->\n\t\t\t\t\t\t\t\t<div ng-if=\"item.showAssignees && item.task.assignees.length !== 0\" class=\"py-1 pl-0 pr-2 pl-0 float-left align-middle\">\n\t\t\t\t\t\t\t\t\t<clang-user-list class=\"users d-inline-block mr-2\" style=\"width: 130px;\" plusnumber=\"{{item.task.assignees.length - 3}}\" on-plus-clicked=\"item.additionalAssigneesClick()\">\n\t\t\t\t\t\t\t\t\t\t<a ng-href=\"{{user.profile_url}}\" ng-repeat=\"assignee in item.task.assignees | limitTo: 3\">\n\t\t\t\t\t\t\t\t\t\t\t<clang-user-list-item img=\"{{assignee.profile_img }}\" name=\"{{assignee.name}}\" profile=\"{{assignee.profile_url}}\"></clang-user-list-item>\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t</clang-user-list>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<div ng-if=\"item.display === 'board'\">\n\n\t<div class=\"task-item form-inline board-task\" as-sortable-item-handle=\"\">\n\t\t<div class=\"list-draggable-area card rounded-corners d-block w-100\" ng-attr-data-no-drag=\"{{item.noDrag()}}\">\n\t\t\t<span class=\"card-status-border\" ng-style=\"{'background-color': item.list.list.colour}\"></span>\n\t\t\t<div class=\"card-body\">\n\t\t\t\t<div class=\"row\">\n\t\t\t\t\t<div class=\"col-md-12\">\n\t\t\t\t\t\t<div class=\"text-break board-task-title\">\n\t\t\t\t\t\t\t<a ng-if=\"item.task.title !== item.task.title_truncated\" ng-href=\"/projects/task/{{item.task.id}}\" rel=\"tooltip\" data-original-title=\"{{item.task.title}}\">{{item.task.title_truncated}}</a>\n\t\t\t\t\t\t\t<a ng-if=\"item.task.title === item.task.title_truncated\" ng-href=\"/projects/task/{{item.task.id}}\">{{item.task.title}}</a>\n\t\t\t\t\t\t\t<a ng-href=\"{{item.task.source.link}}\" ng-show=\"(item.showSource) && (item.task.source) && (item.task.source.name !== '')\">\n\t\t\t\t\t\t\t\t<span class=\"text-muted\">({{item.task.source.name}})</span>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!--- due date -->\n\t\t\t\t\t\t<div class=\"mt-1\" ng-if=\"item.showDate\">\n\t\t\t\t\t\t\t<span ng-if=\"item.task.due_date_formatted != ''\">\n\t\t\t\t\t\t\t\t<span ng-class=\"{'text-danger': item.task.due_date_type=='overdue'}\">\n\t\t\t\t\t\t\t\t\t<span>Due: </span> {{item.task.due_date_formatted}}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"row mt-2\">\n\t\t\t\t\t<div class=\"col-7\">\n\t\t\t\t\t\t<!--- assigned to -->\n\t\t\t\t\t\t<div ng-if=\"item.showAssignees\" class=\"mt-1\">\n\t\t\t\t\t\t\t<clang-user-list class=\"users d-inline-block mr-2\" style=\"width:145px;\" plusnumber=\"{{item.task.assignees.length - 3}}\" on-plus-clicked=\"item.additionalAssigneesClick()\">\n\t\t\t\t\t\t\t\t<a ng-href=\"{{user.profile_url}}\" ng-repeat=\"assignee in item.task.assignees | limitTo: 3\">\n\t\t\t\t\t\t\t\t\t<clang-user-list-item img=\"{{assignee.profile_img }}\" name=\"{{assignee.name}}\" profile=\"{{assignee.profile_url}}\"></clang-user-list-item>\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t</clang-user-list>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"col-5\">\n\t\t\t\t\t\t<!--- Status indicator -->\n\t\t\t\t\t\t<span class=\"float-right mt-1\" ng-switch=\"item.task.priority\">\n\t\t\t\t\t\t\t<cla-status-indicator color=\"#5cb85c\" badgestyle=\"true\" size=\"x-small\" ng-switch-when=\"1\"><small>Low</small></cla-status-indicator>\n\t\t\t\t\t\t\t<cla-status-indicator color=\"#f0ad4e\" badgestyle=\"true\" size=\"x-small\" ng-switch-when=\"2\"><small>Medium</small></cla-status-indicator>\n\t\t\t\t\t\t\t<cla-status-indicator color=\"#d9534f\" badgestyle=\"true\" size=\"x-small\" ng-switch-when=\"3\"><small>High</small></cla-status-indicator>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n</div>\n<!--- END View Task -->\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/taskAddButton.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/taskAddButton.directive.html';
var html = "<button class=\"btn btn-secondary btn-block btn-add-task\" ng-attr-id=\"{{add.id}}\" ng-click=\"add.startTaskEdit($event, add.sectionId)\">\n\t<ion-icon name=\"add-outline\"></ion-icon>\n\t<lmsg src=\"projects.task.add_task\">[Add Task]</lmsg>\n</button>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/taskEdit.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/taskEdit.directive.html';
var html = "<div class=\"modal fade js-task-edit-modal\" id=\"task-edit-modal\" tabindex=\"-2\" role=\"dialog\" aria-labelledby=\"task-edit-modal\">\n\t<div class=\"modal-dialog modal-xl\" role=\"document\">\n\t\t<div class=\"modal-content\" ng-if=\"edit.visible\">\n\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" id=\"more-info--label\">\n\t\t\t\t\t<span lmsg=\"projects.task.modal.add_task\" ng-if=\"edit.task.id==0\">[Add task]</span>\n\t\t\t\t\t<span lmsg=\"projects.task.modal.edit_task\" ng-if=\"edit.task.id!=0\">[Edit task]</span>\n\t\t\t\t</h5>\n\t\t\t\t<button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n\t\t\t\t\t<span aria-hidden=\"true\">\n\t\t\t\t\t\t<ion-icon name=\"close-outline\"></ion-icon>\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div class=\"modal-body\">\n\t\t\t\t<div class=\"form-horizontal\">\n\t\t\t\t\t<form id=\"task-edit-form\" class=\"form-horizontal\" name=\"taskForm\">\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"\" class=\"col-form-label col-lg-2\" lmsg=\"projects.task.edit.task_name\">[Task Name]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-lg-10\">\n\t\t\t\t\t\t\t\t<input type=\"text\" maxlength=\"255\" class=\"form-control\" placeholder=\"Task Name\" ng-model=\"edit.task.title\" lmsg=\"placeholder:projects.task.edit.task_name\">\n\t\t\t\t\t\t\t\t<div ng-show=\"edit.task.title.length > 255\">\n\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<div class=\"alert alert-warning\">\n\t\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.warning.field_longer_than_255\">[Length warning]</lmsg>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"taskDescription\" class=\"col-form-label col-lg-2\" lmsg=\"projects.task.edit.description\">[Description]</label>\n\t\t\t\t\t\t\t<div class=\"col-lg-10\">\n\t\t\t\t\t\t\t\t<cla-html-editor ng-model=\"edit.task.description\" application=\"projects\" control=\"edit.htmlControl\"></cla-html-editor>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-user-picker-inner\" class=\"col-form-label col-lg-2\" name=\"projects:task:assigned_to\" lmsg=\"projects.task.assigned_to\">[Assigned to]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-lg-10\">\n\t\t\t\t\t\t\t\t<clang-projects-user-picker select_id=\"{{edit.listPrefix}}user-picker-{{edit.unique_id}}\" ng-model=\"edit.task.assignees\" ng-change=\"taskAssigneeChanged(edit.task)\" project_id=\"{{edit.task.project_id}}\" user_name=\"{{edit.task.assigned_to.name}}\" allow_unset=\"0\" control=\"edit.userControl\"></clang-projects-user-picker>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"{{edit.listPrefix}}due_date-{{edit.unique_id}}-inner\" class=\"col-form-label col-lg-2\" lmsg=\"projects.task.properties.due_date\">[Due date]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-lg-4\">\n\t\t\t\t\t\t\t\t<cla-date-picker id=\"task-edit-due_date\" ng-model=\"edit.task.due_date\" control=\"edit.task.control\"></cla-date-picker>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-priority\" class=\"col-form-label col-lg-2\" lmsg=\"projects.task.properties.priority\">[Priority]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-lg-4\">\n\t\t\t\t\t\t\t\t<div class=\"form-inline\">\n\t\t\t\t\t\t\t\t<select id=\"task-edit-priority\" name=\"priority\" ng-model=\"edit.task.priority\" class=\"form-control js-task-priorities js-task-edit-priority\">\n\t\t\t\t\t\t\t\t\t<option value=\"0\" lmsg=\"projects.task.priority.none\">[No priority]</option>\n\t\t\t\t\t\t\t\t\t<option value=\"1\" lmsg=\"projects.task.priority.low\">[Low priority]</option>\n\t\t\t\t\t\t\t\t\t<option value=\"2\" lmsg=\"projects.task.priority.medium\">[Medium priority]</option>\n\t\t\t\t\t\t\t\t\t<option value=\"3\" lmsg=\"projects.task.priority.high\">[High priority]</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<div class=\"col-md-12 col-sm-12\" ng-if=\"edit.task.project_id == 0\"></div>\n\t\t\t\t\t\t\t<div class=\"offset-lg-2 col-lg-10\" ng-if=\"edit.task.project_id > 0\">\n\t\t\t\t\t\t\t\t<div ng-if=\"edit.task.project_id > 0\">\n\t\t\t\t\t\t\t\t<div class=\"checkbox\">\n\t\t\t\t\t\t\t\t\t<label for=\"task-edit-private\">\n\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" id=\"task-edit-private\" ng-model=\"edit.task.is_private\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon name=\"lock-closed-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.task.properties.is_private\">[Private Task]</lmsg>\n\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p class=\"text-muted\">\n\t\t\t\t\t\t\t\t\t<span lmsg=\"projects.task.properties.is_private_message\">[Users can always see tasks assigned to them]</span>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-duration\" class=\"col-lg-2 col-form-label\" lmsg=\"projects.task.expected_duration\">\n\t\t\t\t\t\t\t\t[expected duration]\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<div class=\"col-lg-10\">\n\t\t\t\t\t\t\t\t<div class=\"form-inline\">\n\t\t\t\t\t\t\t\t<input type=\"text\" name=\"duration\" id=\"task-edit-duration\" class=\"form-control mr-2\" size=\"2\" ng-model=\"edit.task.duration\" ng-pattern=\"/^$|^(0|[1-9]\\d*)([.]\\d+)?$/\" value=\"0\">\n\t\t\t\t\t\t\t\t<span ng-class=\"{'hide-unit': edit.task.project_id < 1}\">{{edit.task.duration_units}}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p class=\"text-danger\" ng-if=\"taskForm.duration.$invalid\">\n\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.task.duration_value_warning\">[Please input a valid duration value (number)]</lmsg>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"modal-footer\">\n\t\t\t\t<button class=\"btn btn-primary\" ng-click=\"edit.saveTaskEdit($event, task)\" ng-disabled=\"taskForm.duration.$invalid\" lmsg=\"projects.task.save_task\">[Save]</button>\n\t\t\t\t<button class=\"btn btn-danger\" rel=\"tooltip\" ng-click=\"edit.deleteTask($event, task)\" ng-show=\"edit.task.perms.can_edit\"><lmsg src=\"projects.task.delete\">[Delete]</lmsg></button>\n\t\t\t\t<button class=\"btn btn-link\" ng-click=\"edit.cancelTaskEdit($event, task)\" lmsg=\"projects.task.cancel\">[Cancel]</button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/directives/topics_table.directive.html":
/***/ (function(module, exports) {

var path = '/html/directives/topics_table.directive.html';
var html = "<div name=\"component_name\" id=\"topic_table_container\" ng-controller=\"DataTableController as controller\" ng-init=\"controller.init('topic_table', {project_id: ctrl.project_id})\">\n\t<div ng-if=\"ctrl.templates_loaded\">\n\t\t<div class=\"table-controls text-left\" name=\"table_controls\">\n\t\t\t<div class=\"row p-3\">\n\t\t\t\t<div class=\"col-sm-4 col-6 mb-2 mb-sm-0\">\n\t\t\t\t\t<cla-dt-per-page name=\"perpage\" table=\"topic_table\"></cla-dt-per-page>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"col-sm-4 mb-2 mb-sm-0\">\n\t\t\t\t\t<cla-dt-search name=\"search\" table=\"topic_table\"></cla-dt-search>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"col-sm-4\">\n\t\t\t\t<cla-dt-paging name=\"paging\" table=\"topic_table\"></cla-dt-paging>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"table-responsive\">\n\t\t\t<cla-data-table id=\"topic_table\" name=\"datatable\" columns=\"ctrl.columns\" endpoint=\"/api/datatable/dtsource/projects.datatable.topics\" per=\"10\" server-side=\"1\">\n\n\t\t\t\t<table id=\"topics-datatable\" class=\"table table-bordered topics-table p-3 w-100\">\n\t\t\t\t\t<colgroup>\n\t\t\t\t\t\t<col ng-repeat=\"title in ctrl.column_defs\" ng-bind=\"title\">\n\t\t\t\t\t</colgroup>\n\t\t\t\t</table>\n\t\t\t</cla-data-table>\n\n\t\t\t<cla-dt-info name=\"info\" class=\"topic-table-info px-2\" table=\"topic_table\"></cla-dt-info>\n\t\t</div>\n\t</div>\n\n\t<script type=\"claro/template\" ng-repeat=\"template in ctrl.cellTemplates\" name=\"template_container\" id=\"{{template.id}}\" ng-bind=\"template.body | sanitize\"></script>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks.directive.html';
var html = "<div ng-class=\"{'container-center' : tasks.viewMode==='list'}\">\n\t<div class=\"row\">\n\t\t<div class=\"custom-toggle-position col-sm-12\">\n\t\t\t<div class=\"ml-auto\">\n\t\t\t<span class=\"d-inline-block align-middle mr-3\">\n\t\t\t<clang-toggle-switch ng-if=\"tasks.viewMode === 'list'\" ng-model=\"tasks.showCompleted\" name=\"show_complete\" labels=\"showhide\" ng-change=\"tasks.onToggleVisibility($event)\">\n\t\t\t\t<span class=\"font-weight-bold\" lmsg=\"projects.task.completed\">[Completed]</span>\n\t\t\t</clang-toggle-switch>\n\t\t\t<clang-toggle-switch ng-if=\"tasks.viewMode === 'board'\"  ng-model=\"tasks.groupByList\" name=\"group_by_list\" ng-change=\"tasks.onToggleVisibility($event)\">\n\t\t\t\t<span class=\"font-weight-bold\" lmsg=\"projects.tasks.group_by_list\">[Group by List]</span>\n\t\t\t</clang-toggle-switch>\n\t\t\t</span>\n\n\t\t\t<span class=\"align-middle d-inline-block\">\n\t\t\t\t<clang-tasks-list-type ng-model=\"tasks.viewMode\" id=\"task_view_type\"></clang-tasks-list-type>\n\t\t\t</span>\n\t\t</div>\n\t</div>\n\n\t<clang-project-tasks-list class=\"w-100 px-3\" ng-if=\"tasks.viewMode === 'list'\" tasks=\"tasks.formattedTasks\" perms=\"tasks.perms\" is-mobile=\"tasks.isMobile\"></clang-project-tasks-list>\n\t<clang-project-tasks-board class=\"w-100 px-3\" ng-if=\"tasks.viewMode === 'board'\" tasks=\"tasks.formattedTasks\" is-mobile=\"tasks.isMobile\" statuses=\"tasks.statuses\" lists=\"tasks.lists\" group-by-list=\"tasks.groupByList\" perms=\"tasks.perms\"></clang-project-tasks-board>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks.list_type.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks.list_type.directive.html';
var html = "<div class=\"btn-toolbar float-right\">\n\t<div class=\"btn-group\">\n\t\t<a ng-class=\"{active: ctrl.ngModel === 'list'}\" ng-click=\"ctrl.changeView('list',$event);\"\n\t\t   class=\"btn btn-secondary\" ng-href=\"#\">\n\t\t\t <ion-icon name=\"list-circle-outline\"></ion-icon>\n\t\t\t <lmsg src=\"projects.tasks.view.list\">[List]</lmsg>\n\t\t</a>\n\n\t\t<a ng-class=\"{active: ctrl.ngModel === 'board'}\" ng-click=\"ctrl.changeView('board',$event)\"\n\t\t   class=\"btn btn-secondary\" ng-href=\"#\">\n\t\t\t <ion-icon name=\"albums-outline\"></ion-icon>\n\t\t\t <lmsg src=\"projects.tasks.view.board\"> [Kanban]</lmsg>\n\t\t</a>\n\t</div>\n\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks_board.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks_board.directive.html';
var html = "<div class=\"tasks-board\">\n\t<div class=\"tasks-board-scrollable\">\n\t\t<clang-tasks-board-status-header status=\"ctrl.todoStatus\"></clang-tasks-board-status-header>\n\t\t<div class=\"tasks-board-scrollable\" as-sortable=\"ctrl.dragStatusListeners\" ng-model=\"ctrl.orderableStatuses\" is-disabled=\"{{!ctrl.HasStatusDragPerms()}}\">\n\t\t\t<span class=\"tasks-board-draggable-heading\" ng-repeat=\"status in ctrl.orderableStatuses\" as-sortable-item=\"status\">\n\t\t\t\t<clang-tasks-board-status-header status=\"status\" task-count=\"status.taskCount\" allow-drag=\"!ctrl.isMobile\"></clang-tasks-board-status-header>\n\t\t\t</span>\n\t\t</div>\n\t\t<clang-tasks-board-status-header status=\"ctrl.doneStatus\"></clang-tasks-board-status-header>\n\t</div>\n\n\t<div ng-if=\"ctrl.groupByList\">\n\t\t<div as-sortable=\"ctrl.dragSectionListeners\" ng-model=\"ctrl.tasks.lists\" is-disabled=\"{{!ctrl.HasListDragPerms()}}\" >\n\t\t\t<div ng-repeat=\"list in ctrl.tasks.lists\" as-sortable-item=\"section\">\n\t\t\t\t<clang-tasks-board-section list=\"list\" allow-drag=\"!ctrl.isMobile\">\n\t\t\t\t\t<div class=\"tasks-board-scrollable tasks-board-tasks\">\n\t\t\t\t\t\t<clang-tasks-board-status status=\"status\" list=\"list\" ng-repeat=\"status in list.statuses\">\n\t\t\t\t\t\t\t<div class=\"board-task-drop-container js-draggable\" as-sortable=\"ctrl.dragTaskListeners\" ng-model=\"status.tasks\" is-disabled=\"{{!ctrl.HasTaskDragPerms()}}\" data-list-id=\"{{list.list.id}}\">\n\t\t\t\t\t\t\t\t<div ng-repeat=\"task in status.tasks\" as-sortable-item=\"task\">\n\t\t\t\t\t\t\t\t\t<project-task-item task=\"task\" display=\"board\" list=\"list\" use-drag-and-drop=\"{{ctrl.perms.can_edit_any_task}}\" task_delete=\"ctrl.taskDeletedCallback(task);\" task_complete_toggled=\"ctrl.taskCompleteToggledCallback(task);\" allow-drag=\"!ctrl.isMobile && ctrl.HasTaskDragPerms(task)\"></project-task-item>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</clang-tasks-board-status>\n\t\t\t\t\t</div>\n\t\t\t\t</clang-tasks-board-section>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\n\t<div ng-if=\"!ctrl.groupByList\">\n\t\t<div class=\"tasks-board-scrollable tasks-board-tasks mt-2\">\n\t\t\t<clang-tasks-board-status status=\"status\" ng-repeat=\"status in ctrl.tasks.lists[0].statuses\">\n\t\t\t\t<div class=\"js-draggable\" as-sortable=\"ctrl.dragTaskListeners\" ng-model=\"status.tasks\" is-disabled=\"{{!ctrl.HasTaskDragPerms()}}\">\n\t\t\t\t\t<div ng-repeat=\"task in status.tasks\" as-sortable-item=\"task\">\n\t\t\t\t\t\t<project-task-item task=\"task\" display=\"board\" list=\"list\" use-drag-and-drop=\"{{ctrl.perms.can_edit_any_task == 1}}\" task_delete=\"ctrl.taskDeletedCallback(task);\" task_complete_toggled=\"ctrl.taskCompleteToggledCallback(task);\" allow-drag=\"!ctrl.isMobile && ctrl.HasTaskDragPerms(task)\"></project-task-item>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</clang-tasks-board-status>\n\t\t</div>\n\t</div>\n\n\t<div class=\"tasks-board-saving text-center\" ng-if=\"ctrl.tasksService.tasksUpdating\">\n\t\t<b lmsg=\"projects.tasks.saving_changes\">[Saving changes...]</b>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks_board_section.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks_board_section.directive.html';
var html = "<div class=\"task-board-list\">\n\t<div class=\"task-list-header mb-1\" as-sortable-item-handle=\"\">\n\t\t<div ng-attr-data-no-drag=\"{{ctrl.noDrag()}}\">\n\t\t\t<span class=\"btn btn-link\" ng-if=\"ctrl.list.list.expanded\" ng-click=\"ctrl.collapseList(ctrl.list.list.id)\">\n\t\t\t\t<ion-icon name=\"chevron-down-outline\"></ion-icon>\n\t\t\t</span>\n\t\t\t<span class=\"btn btn-link\" ng-if=\"!ctrl.list.list.expanded\" ng-click=\"ctrl.expandList(ctrl.list.list.id)\">\n\t\t\t\t<ion-icon name=\"chevron-up-outline\"></ion-icon>\n\t\t\t</span>\n\t\t\t<span class=\"mt-1 mb-1 d-inline-block align-middle mr-3\">\n\t\t\t\t<span class=\"status-dot\" style=\"background: {{ctrl.list.list.colour}};\"></span>\n\t\t\t</span>\n\n\n\t\t<span ng-if=\"!ctrl.editMode\" ng-bind=\"ctrl.list.listName\"></span>\n\t\t<span class=\"d-inline-block\">\n\t\t<div ng-if=\"ctrl.editMode\" class=\"form-inline\" no-drag>\n\t\t\t<input class=\"form-control mr-2\" type=\"text\" ng-attr-id=\"list_{{ctrl.list.listId}}\" ng-model=\"ctrl.editName\" />\n\t\t\t<cla-colour-picker class=\"task-section-edit-colour mr-2\" ng-model=\"ctrl.editColour\"></cla-colour-picker>\n\t\t\t<a href=\"#0\" class=\"btn btn-link\" ng-click=\"ctrl.edit($event)\">\n\t\t\t\t<ion-icon name=\"checkmark-circle-outline\"></ion-icon>\n\t\t\t</a>\n\t\t\t<a href=\"#0\" class=\"btn btn-link\" ng-click=\"ctrl.cancelEdit($event)\">\n\t\t\t\t<ion-icon name=\"close-circle-outline\"></ion-icon>\n\t\t\t</a>\n\t\t</div>\n\t\t</span>\n\n\t\t<span class=\"d-inline-block align-middle ml-10\">\n\t\t\t<cla-menu ng-if=\"ctrl.hasEditPerms\" ng-attr-no-drag=\"{{ ctrl.editMode ? '' : undefined }}\">\n\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:pin_project\" ng-href=\"#0\" ng-click=\"ctrl.startEdit($event)\">\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.edit_list\">[Edit list]</lmsg>\n\t\t\t\t</a>\n\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:pin_project\" ng-href=\"#0\" ng-click=\"ctrl.delete($event)\">\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.delete_list\">[Delete list]</lmsg>\n\t\t\t\t</a>\n\t\t\t</cla-menu>\n\t\t</span>\n\t\t</div>\n\t</div>\n\n\t<ng-transclude ng-if=\"ctrl.list.list.expanded\"></ng-transclude>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks_board_status.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks_board_status.directive.html';
var html = "<div class=\"board-status-column\">\n\t<ng-transclude></ng-transclude>\n</div>\n\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks_board_status_header.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks_board_status_header.directive.html';
var html = "\n\n<div ng-if=\"ctrl.isDraggable()\" as-sortable-item-handle=\"\">\n\t<div class=\"board-status-column board-status-header\" ng-attr-no-drag=\"{{ ctrl.editMode ? '' : undefined }}\">\n\t\t<div class=\"media board-status-media\">\n\t\t\t<div class=\"mr-3\">\n\t\t\t\t<span ng-if=\"ctrl.status.id < 0 && ctrl.status.id === -2\">\n\t\t\t\t\t<ion-icon class=\"text-muted\" name=\"checkmark-done-circle-outline\"></ion-icon>\n\t\t\t\t</span>\n\t\t\t\t<span ng-if=\"ctrl.status.id < 0 && ctrl.status.id === -1\">\n\t\t\t\t\t<ion-icon class=\"text-muted\" name=\"list-circle-outline\"></ion-icon>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div class=\"media-body align-middle\">\n\t\t\t\t<div ng-if=\"!ctrl.editMode\">\n\t\t\t\t\t<span ng-bind=\"ctrl.status.localized_name\">[List name]</span>\n\t\t\t\t</div>\n\t\t\t\t<div ng-if=\"ctrl.editMode\" class=\"form-inline\">\n\t\t\t\t\t<input type=\"text\" class=\"form-control board-status-input\" id=\"status_header_{{ctrl.status.id}}\" ng-model=\"ctrl.editName\" />\n\t\t\t\t\t<a href=\"#0\" class=\"btn btn-link btn-sm btn-board-status\" title=\"Save\" ng-click=\"ctrl.rename($event)\">\n\t\t\t\t\t\t<ion-icon name=\"checkmark-circle-outline\"></ion-icon>\n\t\t\t\t\t</a>\n\t\t\t\t\t<a href=\"#0\" class=\"btn btn-link btn-sm btn-board-status\" title=\"Cancel\" ng-click=\"ctrl.cancelRename($event)\">\n\t\t\t\t\t\t<ion-icon name=\"close-circle-outline\"></ion-icon>\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"ml-2\">\n\t\t\t\t<cla-menu slot=\"menu\" close-on-select ng-if=\"ctrl.hasEditPerms\" ng-attr-no-drag=\"{{ ctrl.editMode ? '' : undefined }}\">\n\t\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:unpin_project\" ng-href=\"#\" ng-if=\"ctrl.status.id !== -1\" ng-click=\"ctrl.addStatusBefore($event)\">\n\t\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"arrow-back-outline\"></ion-icon>\n\t\t\t\t\t\t<lmsg src=\"projects.tasks.board.add_status_before\">[Add status before]</lmsg>\n\t\t\t\t\t</a>\n\t\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:unpin_project\" ng-href=\"#\" ng-if=\"ctrl.status.id !== -2\" ng-click=\"ctrl.addStatusAfter($event)\">\n\t\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"arrow-forward-outline\"></ion-icon>\n\t\t\t\t\t\t<lmsg src=\"projects.tasks.board.add_status_after\">[Add status after]</lmsg>\n\t\t\t\t\t</a>\n\t\t\t\t\t<a class=\"dropdown-item\" ng-href=\"#\" ng-click=\"ctrl.startRename($event)\">\n\t\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"text-outline\"></ion-icon>\n\t\t\t\t\t\t<lmsg src=\"projects.tasks.board.rename_status\">[Rename status]</lmsg>\n\t\t\t\t\t</a>\n\t\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:pin_project\" ng-href=\"#\" ng-if=\"ctrl.status.id > 0\" ng-click=\"ctrl.delete($event)\">\n\t\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"trash-outline\"></ion-icon>\n\t\t\t\t\t\t<lmsg src=\"projects.tasks.board.delete_status\">[Delete status]</lmsg>\n\t\t\t\t\t</a>\n\t\t\t\t</cla-menu>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n\n<div class=\"board-status-column board-status-header\" ng-if=\"!ctrl.isDraggable()\">\n\t<div class=\"media board-status-media\">\n\t\t<div class=\"mr-3\">\n\t\t\t<span ng-if=\"ctrl.status.id < 0 && ctrl.status.id === -2\">\n\t\t\t\t<ion-icon class=\"text-muted\" name=\"checkmark-done-circle-outline\"></ion-icon>\n\t\t\t</span>\n\t\t\t<span ng-if=\"ctrl.status.id < 0 && ctrl.status.id === -1\">\n\t\t\t\t<ion-icon class=\"text-muted\" name=\"list-circle-outline\"></ion-icon>\n\t\t\t</span>\n\t\t</div>\n\t\t<div class=\"media-body align-middle\">\n\t\t\t<div ng-if=\"!ctrl.editMode\">\n\t\t\t\t<span ng-bind=\"ctrl.status.localized_name\">[List name]</span>\n\t\t\t</div>\n\t\t\t<div ng-if=\"ctrl.editMode\" class=\"form-inline\"å>\n\t\t\t\t<input type=\"text\" id=\"status_header_{{ctrl.status.id}}\" class=\"form-control board-status-input\" ng-model=\"ctrl.editName\" />\n\t\t\t\t<a href=\"#0\" class=\"btn btn-link btn-sm btn-board-status\" title=\"Save\" ng-click=\"ctrl.rename($event)\">\n\t\t\t\t\t<ion-icon name=\"checkmark-circle-outline\"></ion-icon>\n\t\t\t\t</a>\n\t\t\t\t<a href=\"#0\" class=\"btn btn-link btn-sm btn-board-status\" title=\"Cancel\" ng-click=\"ctrl.cancelRename($event)\">\n\t\t\t\t\t<ion-icon name=\"close-circle-outline\"></ion-icon>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"ml-2\">\n\t\t\t<cla-menu slot=\"menu\" close-on-select ng-if=\"ctrl.hasEditPerms\">\n\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:unpin_project\" ng-href=\"#0\" ng-if=\"ctrl.status.id !== -1\" ng-click=\"ctrl.addStatusBefore($event)\">\n\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"arrow-back-outline\"></ion-icon>\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.add_status_before\">[Add status before]</lmsg>\n\t\t\t\t</a>\n\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:unpin_project\" ng-href=\"#0\" ng-if=\"ctrl.status.id !== -2\" ng-click=\"ctrl.addStatusAfter($event)\">\n\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"arrow-forward-outline\"></ion-icon>\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.add_status_after\">[Add status after]</lmsg>\n\t\t\t\t</a>\n\t\t\t\t<a class=\"dropdown-item\" ng-href=\"#0\" ng-click=\"ctrl.startRename($event)\">\n\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"text-outline\"></ion-icon>\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.rename_status\">[Rename status]</lmsg>\n\t\t\t\t</a>\n\t\t\t\t<a class=\"dropdown-item\" lmsg=\"projects:project:pin_project\" ng-href=\"#0\" ng-if=\"ctrl.status.id > 0\" ng-click=\"ctrl.delete($event)\">\n\t\t\t\t\t<ion-icon class=\"align-bottom\" name=\"trash-outline\"></ion-icon>\n\t\t\t\t\t<lmsg src=\"projects.tasks.board.delete_status\">[Delete status]</lmsg>\n\t\t\t\t</a>\n\t\t\t</cla-menu>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./html/project/tasks_list.directive.html":
/***/ (function(module, exports) {

var path = '/html/project/tasks_list.directive.html';
var html = "<div class=\"list-unstyled task-list\" as-sortable=\"ctrl.dragSectionListeners\" ng-model=\"ctrl.tasks.lists\" is-disabled=\"{{!ctrl.HasListDragPerms()}}\">\n\t<div ng-repeat=\"list_entry in ctrl.tasks.lists\" as-sortable-item=\"section\">\n\t\t<clang-tasks-board-section list=\"list_entry\" allow-drag=\"!ctrl.isMobile\">\n\t\t\t<project-task-edit class=\"new-task\" ng-show=\"list_entry.list.new_task.is_edit\" task=\"list_entry.list.new_task\" add_mode=\"true\" id=\"new_task-{{list_entry.list.id}}\" list_prefix=\"section\" task_update=\"ctrl.taskAddedCallback(task);\" task_delete=\"item.taskDeleted(task);\"></project-task-edit>\n\n\t\t\t<ul class=\"list-unstyled task-list-items js-draggable\" as-sortable=\"ctrl.dragTaskListeners\" ng-model=\"list_entry.tasks\" is-disabled=\"{{!ctrl.HasTaskDragPerms()}}\" data-list-id=\"{{list.list.id}}\">\n\t\t\t\t<li ng-repeat=\"task in list_entry.tasks\"\n\t\t\t\t    class=\"task-list-item as-sortable-item\"\n\t\t\t\t    as-sortable-item=\"task\">\n\t\t\t\t\t<project-task-item task=\"task\" use-drag-and-drop=\"{{ctrl.perms.can_edit_any_task == 1}}\" task_delete=\"ctrl.taskDeletedCallback(task);\" task_complete_toggled=\"ctrl.taskCompleteToggledCallback(task);\" allow-drag=\"!ctrl.isMobile && ctrl.HasTaskDragPerms(task)\"></project-task-item>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</clang-tasks-board-section>\n\t</div>\n</div>\n\n<div class=\"tasks-board-saving text-center\" ng-if=\"ctrl.tasksService.tasksUpdating\">\n\t<b lmsg=\"projects.tasks.saving_changes\">[Saving...]</b>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/controller/historyCtrl.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.filter("sanitize", ['$sce', sanitizeFilter])
		.controller("historyCtrl", ["$scope", "$http", historyController]);

	function sanitizeFilter($sce)
	{
		return function(htmlCode){
			// Convert repeated metadata from an object to a string
			if (typeof htmlCode === 'object')
				htmlCode = htmlCode[Object.keys(htmlCode)[0]];
			return $sce.trustAsHtml(htmlCode);
		}
	}

	function historyController($scope, $http)
	{
		$scope.history = history_items;
		$scope.updateHistoryData = function(data)
		{
			$scope.type = data.type;
			$scope.object_id = data.objectId;
			$scope.itemsPerPage = data.itemsPerPage;
			$scope.currentPage = data.currentPage;
			$scope.totalItems = data.totalItems;
		};
		$scope.updateHistoryData(history_data);

		// Load new history data
		function getResultsPage(pageNumber)
		{
			var type = '';
			if ($scope.type === 'task')
				type = $scope.type + '/';

			$http.get('/api/projects/' + type + $scope.object_id + '/history?page=' + pageNumber)
				.then(function(result) {
					$scope.history = result.data.history;
					$scope.updateHistoryData(result.data.history_data);
				});
		}

		// If pagination is clicked
		$scope.pageChanged = function(newPage)
		{
			getResultsPage(newPage);
		};

		$scope.$on('updateHistoryLog', function(event, args)
		{
			getResultsPage($scope.currentPage);
		});
	}
}());


/***/ }),

/***/ "./js/controller/projectDetailCtrl.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'projectDetailApp';

	angular.module(moduleName)
		.controller("projectDetailCtrl", ['$scope', '$http', '$timeout', 'breadcrumb.service', 'claProjectDetailsService', 'projectPreloadFactory', 'confirmService', projectDetailController]);

	function projectDetailController($scope, $http, $timeout, breadcrumbService, projectDetailsService, preload, confirmService)
	{
		$scope.project_header = project_header.info;
		$scope.project_perms = preload.perms;
		$scope.more_info_shown = false;
		$scope.summaryIsTooLong = false;
		$scope.editorEnabled = false;
		$scope.has_tasks_to_view = !!preload.tasks.length;
		$scope.show_tasks =
            $scope.project_perms.can_view_tasks ||
            ($scope.project_perms.is_team_member && $scope.has_tasks_to_view);

		// service
		$scope.service = projectDetailsService;
        $scope.currentTab = $scope.service.getLocation();

        // Delayed because the controls might not be instantiated yet to receive the event and they should not miss it
        $timeout(function() {
            angular.element(document).trigger('projectsTabChange', $scope.currentTab);
        }, 0);

        var crumbs = [
            {
                'url': '/projects',
                'name': lmsg('projects.breadcrumb.projects')
            },
            {
                'url': '/projects/' + $scope.project_header.id,
                'name': $scope.project_header.name
            },
            {
                'name': lmsg('projects.breadcrumb.overview')
            }
        ];

        breadcrumbService.setCrumbs(crumbs);

        angular.element(document).on('projectsTabChange', function($event, tabName) {
            if (tabName === 'overview')
                crumbs[2].name = lmsg('projects.breadcrumb.overview');

            if (tabName === 'tasks')
                crumbs[2].name = lmsg('projects.project.tasks');

            if (tabName === 'team')
                crumbs[2].name = lmsg('projects.project.team');

            if (tabName === 'discussion')
                crumbs[2].name = lmsg('projects.project.topics');

            if (tabName === 'files')
                crumbs[2].name = lmsg('projects.project.files');

            if (tabName === 'history')
                crumbs[2].name = lmsg('projects.project.history');

            breadcrumbService.setCrumbs(crumbs);

            $timeout(function() {
                $scope.$digest();
            }, 0);
        });

		$scope.statusClasses =
		{
            1: 'warning',        // Draft
            2: 'success',        // Live
            3: 'info',           // On hold
            4: 'secondary'       // Archived
		};

		$scope.setFollowButtonTitle = function()
		{
			if ($scope.project_header.follow == 0)
				$scope.project_header.follow_tooltip = lmsg('projects.project.follow');
			else
				$scope.project_header.follow_tooltip = lmsg('projects.project.unfollow');
		};
		$scope.setFollowButtonTitle();

		$scope.determineVisibleTab = function(location)
		{
			var  possibleTabs = [];
            if ($scope.project_perms.can_view_tasks)
            possibleTabs.push('overview');
            if ($scope.project_perms.can_view_tasks)
            possibleTabs.push('tasks');
            if ($scope.project_perms.can_view_team)
            possibleTabs.push('team');
            if ($scope.project_perms.can_view_discussion)
            possibleTabs.push('discussion');
            possibleTabs.push('files');
            possibleTabs.push('history');

			// Get first visible tab to use as default
			var tab = possibleTabs[0];

			// Find if one was supplied in the URL
			var regex = /([\w]+)Tab/;
			var matches = regex.exec(location);

			var byUrl = '';
			if ((matches !== null) &&
				(matches.length > 1))
				byUrl = matches[1];

			if (possibleTabs.indexOf(byUrl) > -1)
				tab = byUrl;

            angular.element(document).trigger('projectsTabChange', tab);

            $scope.currentTab = tab;
            $timeout(function() {
                $scope.$digest();
            }, 0);
		};
		$scope.determineVisibleTab($scope.service.getLocation());

        $scope.service.registerCallback(function(location) {
            $scope.determineVisibleTab(location.tab);
        });

		$scope.toggleFollow = function()
		{
			var url = '/api/projects/' + $scope.project_header.id + '/follow';

			if ($scope.project_header.follow == 0)
			{
				$http.put(url)
				.then(function(result)
				{
					angular.element('#follow-button').tooltip('hide');
					$scope.project_header.follow = result.data.follow;
					$scope.setFollowButtonTitle();
				});
			} else
			{
				$http.delete(url)
				.then(function(result)
				{
					angular.element('#follow-button').tooltip('hide');
					$scope.project_header.follow = result.data.follow;
					$scope.setFollowButtonTitle();
				});
			}

			$scope.setFollowButtonTitle();

			$scope.updateHistoryLog();
		};

		$scope.setRag = function(value, event)
		{
			event.preventDefault();

			if (!$scope.project_perms.can_edit_project)
				return;

			var url = '/api/projects/' + $scope.project_header.id + '/rag?value=' + value;
			$http.put(url).then(function(result)
			{
				if (result.data.result == false)
				{
					cla.showMessage(result.data.message, '', true);
				} else
				{
					$scope.project_header.rag_text = result.data.value;
					$scope.updateHistoryLog();
				}
			});
		};

		$scope.saveSummary = function (event)
		{
			event.preventDefault();

			var editor = $('#summaryText');
			editor.sync();
			var summary = editor.val();
			var url = '/api/projects/' + $scope.project_header.id + '/summary';
			$http({
				method: 'POST',
				url: url,
				data: "summary=" + encodeURIComponent(summary),
				headers: {'Content-Type': 'application/x-www-form-urlencoded'}
			}).then(function(result)
			{
				if (result.data.result)
				{
					$scope.project_header.summary = result.data.summary;
					$scope.editorEnabled = !$scope.editorEnabled;
					$scope.updateHistoryLog();
				} else
				{
					cla.showMessage(result.data.message, '', true);
				}
			});
		};

		$scope.cancelSummary = function (event)
		{
			event.preventDefault();

			var editor = $('#summaryText');
			editor.bbcode($scope.project_header.summary.bbcode);

			$scope.editorEnabled = false;
		};

		$scope.toggleMoreInfo = function()
		{
			$scope.more_info_shown = !$scope.more_info_shown;
		};

		$scope.updateHistoryLog = function()
		{
			$scope.$root.$broadcast('updateHistoryLog', null);
		};

		$scope.$on('setTeamTabCount', function(event, args)
		{
            $scope.service.setTeamCount(parseInt(args));
		});

		$scope.$on('setTopicTabCount', function(event, args)
		{
            $scope.service.setTopicCount(parseInt(args));
		});

		$scope.$on('setFilesTabCount', function(event, args)
		{
			// TODO in phase 2 - Keep as 0 for now to not display anything
			//$scope.service.setFileCount(0);
		});

		$scope.$on('setUserPerms', function(event, args)
		{
			$scope.project_perms = args;
		});

		$scope.$on('updateProjectProgress', function(event, progress)
		{
			$scope.project_header.progress = progress;
			// IE doesn't update properly with aria values so set it directly
			angular.element('#progress-bar').css('width', progress + '%');
		});
		// Make IE start off with the right sized progress bar
		$scope.$root.$broadcast('updateProjectProgress', $scope.project_header.progress);

		$scope.isRepeatedMetadata = function(someVar)
		{
			return (typeof someVar === 'object');
		};

		$scope.checkSummaryLength = function()
		{
			var test = angular.element('#summaryText').data('wbb');
			if (typeof test !== 'undefined')
			{
				angular.element('#summaryText').sync();
				$scope.summaryIsTooLong = angular.element('#summaryText').bbcode().length > 2000;
			}
		};

        $scope.confirmDelete = function(event, project_id)
        {
            event.preventDefault();
            var url = '/api/projects/' + project_id;

            confirmService.show({
                title: lmsg('projects.project.delete_project.title'),
                message: lmsg('projects.project.delete_confirm'),
            }).then(function() {
                $http.delete(url).then(function(result)
                {
                    if (result.data.result)
                        window.location.href = '/projects?message=' + encodeURIComponent(lmsg('projects.js.project.project_deleted'));
                    else
                        cla.showMessage(lmsg('common.general.error') + ': ' + result.data.message, '', true);
                });
            }, function() {});
        };
	}
}());


/***/ }),

/***/ "./js/controller/projectFilesCtrl.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'projectDetailApp';

	angular.module(moduleName)
		.controller("projectFilesCtrl", ["$scope", projectFilesController]);

	function projectFilesController($scope)
	{
		$scope.folder_id = project_folder_id;

		// A conflict makes DMSV modals change the URL when used on this page.
		// This code will replace the buttons href attribute and open the modal manually instead
		angular.element('#add-folder-docs-btns [data-toggle="modal"]').each(function()
		{
			var button = angular.element(this);
			var target = button.prop('href');
			var bits = target.split('#');
			if (bits.length !== 2)
				return;
			target = '#' + bits[1];

			button.prop('href', '#');
			button.on('click', function(e)
			{
				e.preventDefault();
				angular.element(target).modal();
			});
		});
	}
}());

/***/ }),

/***/ "./js/controller/projectTasksCtrl.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'projectDetailApp';

	angular.module(moduleName)
		.controller("projectTasksCtrl", ['$scope', '$http', 'Task', 'TaskSection', 'growl.service', 'tasksService', 'projectPreloadFactory', projectTasksController])
        .filter('filterSections', function() {
            return function(sections, showEmpty) {
                if (showEmpty)
                    return sections;

                var out = [];

                for (var i = 0; i < sections.length; i++) {
                    if (sections[i].tasks.length)
                        out.push(sections[i]);
                }

                return out;
            };
        })

	function projectTasksController($scope, $http, Task, TaskSection, growlService, tasksService, preload)
	{
		var ctrl = this;

		/**
		 * Data setup
		 */

		ctrl.show_completed = false;
		ctrl.project_id = preload.id;
		ctrl.duration_units = preload.duration_units;

		ctrl.perms = preload.perms;
		ctrl.can_see_empty_sections = ctrl.perms.can_add_task || ctrl.perms.can_edit_any_task;

        ctrl.tasks = tasksService.tasks;
        ctrl.lists = tasksService.lists;
        ctrl.statuses = tasksService.statuses;
        ctrl.archiveDate = preload.archive_date;

		/**
		 * Task methods
		 */

		ctrl.toggleAddTask = function(section)
		{
			if (!section.new_task.is_edit)
			{
				ctrl.resetNewTask(section);
				ctrl.enableNewTaskPriority(section);
				section.new_task.is_edit = true;
			} else
			{
				section.new_task.is_edit = false;
			}
		};

		ctrl.resetNewTask = function(section)
		{
			section.new_task = new Task();
			section.new_task.section_id = section.id;
			section.new_task.project_id = ctrl.project_id;
			section.new_task.assigned_to_id = null;
			section.new_task.due_date = '';
			section.new_task.assigned_to = {name: '', id: null};
			section.new_task.priority = '0';
			section.new_task.duration_units = ctrl.duration_units;
			if (typeof section.new_task.control.reset !== 'undefined')
			{
				section.new_task.control.reset();
			}
		};

		ctrl.cancelAddTask = function(event, section)
		{
			section.new_task.show = false;
			ctrl.resetNewTask(event, section);

			$('#collapseAddTask-' + section.id).collapse('toggle');
		};

		/**
		 * Section methods
		 */

		ctrl.resetNewSection = function()
		{
			ctrl.new_section = new TaskSection();
			ctrl.new_section.project_id = ctrl.project_id;
		};

		ctrl.showAddSection = function() {
			ctrl.add_section = true;
			setTimeout(function()
			{
				angular.element('#new_section_colour-inner').css('background-color', ctrl.new_section.colour);
			},0);
		};

		ctrl.addSection = function(event)
		{
			event.preventDefault();

			ctrl.new_section.add(function()
			{
				var new_section = angular.extend(new TaskSection(), ctrl.new_section);
				new_section.setDisplayOrder(ctrl.lists.length + 1);
				ctrl.lists.push(new_section);
				ctrl.resetNewTask(new_section);

				ctrl.resetNewSection();
				ctrl.add_section = false;

				angular.element('#add-list').modal('hide');
				growlService.showSuccess(lmsg('projects.project.task_list.created'));
            }, function(data)
			{
			    growlService.showError(data.error);
			});
		};

		ctrl.cancelAddSection = function (event)
		{
			event.preventDefault();
			ctrl.resetNewSection();
			ctrl.add_section = false;
		};

		ctrl.deleteSection = function(event, section)
		{
			event.preventDefault();

			if(confirm(lmsg('projects.js.are_you_sure')))
			{
				angular.element('#delete-section-' + section.id).tooltip('hide');
				section.delete(function(section_id)
				{
					var section_list = [];
					for (var i = 0; i < ctrl.lists.length; i++)
					{
						if (ctrl.lists[i].id != section_id)
							section_list.push(ctrl.lists[i]);
					}
					ctrl.lists = section_list;
					$(this).tooltip('destroy');
				}, function(data)
				{
				    growlService.showError(data.error);
				});
			}
		};

		ctrl.saveSectionEdit = function(event, section)
		{
			event.preventDefault();

			section.update(function()
			{
				section.is_edit = false;
			}, function(data)
			{
			    growlService.showError(data.error);
			});

		};

		ctrl.startSectionEdit = function(event, section)
		{
			event.preventDefault();

			section.makeEditCopy();
			section.is_edit = true;
			setTimeout(function()
			{
				angular.element('#section_colour-' + section.id + '-inner').css('background-color', section.colour);
			},0);
		};

		ctrl.cancelSectionEdit = function(event, section)
		{
			event.preventDefault();

			section.is_edit = false;
		};

		ctrl.resetNewSection();



		ctrl.colourSelect = function(target)
		{
			target.select2({
				templateResult: function (data)
				{
					if (!data.id) { return data.text; }
					return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
				},
				templateSelection: function formatPrioritySelection (data)
				{
					if (!data.id) { return data.text; }
					return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
				},
				escapeMarkup: function(markup)
				{
					return markup;
				},
				minimumResultsForSearch: Infinity // Hide search box
			});
		};

		ctrl.enableNewTaskPriority = function(section)
		{
			var select = $('#section-task-edit-section-' + section.id + ' .js-task-priorities');
			if (select.hasClass('select2-container'))
				return;

			// This should be automatic but it is invisible still at this point so not processed yet.
			select.val(section.new_task.priority);

			ctrl.colourSelect(select);
		};

		ctrl.updateHistoryLog = function()
		{
			$scope.$root.$broadcast('updateHistoryLog', null);
		};

		ctrl.updateProjectProgress = function(progress)
		{
			$scope.$root.$broadcast('updateProjectProgress', progress);
		};
	}
}());


/***/ }),

/***/ "./js/controller/projectTeamCtrl.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'projectDetailApp';

	angular.module(moduleName)
        .filter("sanitize", ['$sce', sanitizeFilter])
		.controller("projectTeamCtrl", ["$scope", "$http", 'growl.service', projectTeamController]);

    function sanitizeFilter($sce)
    {
        return function(htmlCode){
            return $sce.trustAsHtml(htmlCode);
        }
    }

	var entityMap = {
		"&": "&amp;",
		"<": "&lt;",
		">": "&gt;",
		'"': '&quot;',
		"'": '&#39;',
		"/": '&#x2F;'
	};

	var AUTOMATIC_COLOUR_COUNT = 10;

	function escapeHtml(string) {
		return String(string).replace(/[&<>"'\/]/g, function (s) {
			return entityMap[s];
		});
	}

	function projectTeamController($scope, $http, growl)
	{
		$scope.project_members = project_members.team;
		$scope.memberCount = project_members.total_members;
		$scope.currentPage = 1;
		$scope.pageSize = project_members.page_size;
		$scope.project_member_perms = project_members.column_perms;
		$scope.project_perms = project_perms;
		$scope.member_role = '';

		$scope.project_id = project_id;

		// Model for user editing/adding. Because we are using the userpicker component for adding it isn't possible to include that
		$scope.user_values =
		{
			type: 'add',
			role: '',
			responsibility: '',

			// Used for edit only
			user_id: 0,
			profile_url: '',
			name: ''
		};

		// Angulars model won't be connected yet so initialise the select2 after a short delay
		setTimeout(function()
		{
			$('#inputResponsibility').select2(
				{
					ajax: {
						url: "/api/projects/responsibilities",
						dataType: 'json',
						delay: 250,
						data: function (params) {
							return {
								q: params.query, // search term
								page: params.page
							};
						},
						processResults: function (data) {
							return {results: data.data};
						},
						cache: true
					},
					createSearchChoice:function(term, data) {
						if ($(data).filter(function() {
								return this.text.localeCompare(term)===0;
							}).length===0) {
							return {id:term, text:term};
						}
					},
					tags: true,
					multiple: false,
					placeholder: '(optional)',
					allowClear: true,
					maximumSelectionSize: 1,
					minimumInputLength: 0
				}
			);
		}, 50);

		$scope.updateTeamCount = function()
		{
			var count = $scope.memberCount;
			$scope.$root.$broadcast('setTeamTabCount', count);
		};

		$scope.updateTeamCount();

		$scope.showEditMember = function(event, member)
		{
			event.preventDefault();
			// Hide the tooltip

			angular.element('#edit-link-' + member.user_id).tooltip('hide');

			$scope.user_values.user_id = member.user_id;
			$scope.user_values.profile_url = member.profile_url;
			$scope.user_values.name = member.full_name;
			$scope.user_values.responsibility = member.project_responsibility;
			var responsibility = angular.element('#inputResponsibility');
			responsibility.html('');
			responsibility.append('<option selected value="'+member.project_responsibility+'">'+escapeHtml(member.project_responsibility)+'</option>');
			setTimeout(function(){angular.element('#inputResponsibility').trigger('change');}, 0);

			angular.element('#teamModal').modal('show');
		};

		$scope.editMembers = function(event)
		{
			event.preventDefault();

			// get selected responsibility from tab Team
			$scope.user_values.responsibility = angular.element('#inputResponsibility').val();

            if ($scope.user_values.responsibility === null) {
                $scope.user_values.responsibility = '';
            }

			var users = [];

			users.push($scope.user_values.user_id);

			$('#teamModal').modal('hide');

			for (var i = 0; i < users.length; i++)
			{
				var url = '/api/projects/' + $scope.project_id + '/member/' + users[i];
				$http.put(url, {}, {
                    params: {
                        responsibility: $scope.user_values.responsibility
                    }
				}).then(function(result)
				{
					if (result.data.result)
					{
						// Overwrite the user if they appear in the list
                        // We must preserve the list order, and the user might not
                        // be on the currently loaded page (because function is
                        // called asynchronously
					    for (var j = 0; j < $scope.project_members.length; j++)
						{
							if ($scope.project_members[j].user_id == result.data.entry.user_id)
								$scope.project_members[j] = result.data.entry;
						}

						$scope.updateHistoryLog();
					} else
					{
						cla.showMessage(result.data.message, '', true);
					}
				});
			}
		};

		$scope.updateHistoryLog = function()
		{
			$scope.$root.$broadcast('updateHistoryLog', null);
		};

		$scope.getColourForRole = function(roleName)
        {
            // Fast hash algorithm from https://stackoverflow.com/a/8831937

            // For this automatic version of colours we are getting a hash of the role name and using modular division to reduce it to the size
            // of our colour table. The reason for doing this rather then using ID or position of the role in the project is to keep the
            // colours consistent between projects for the same role name. so "PM" will always be the same colour regardless of where it is

            var hash = 0;
            if (roleName.length === 0) {
                return hash;
            }
            for (var i = 0; i < roleName.length; i++) {
                var char = roleName.charCodeAt(i);
                hash = ((hash<<5)-hash)+char;
                hash = hash & hash; // Convert to 32bit integer
            }

            return (Math.abs(hash) % AUTOMATIC_COLOUR_COUNT) + 1;
        };

		$scope.pageChanged = function(newPage) {
		    $("#team-list-spinner").show();
		    $("#team-list-table").addClass("loading-table");
		    $http({
                method: 'GET',
                url: '/api/projects/v2/' + $scope.project_id + '/members',
                params: {
                    offset: (newPage - 1) * $scope.pageSize,
                    limit: $scope.pageSize
                }
            }).then(function(data) {
                if (data.data) {
                    var members = data.data.data;

                    $scope.project_members.length = 0;
                    for (var i = 0; i < members.length; i++)
                        $scope.project_members.push(members[i])

                    $("#team-list-spinner").hide();
                    $("#team-list-table").removeClass("loading-table");
                }
            }, function(data) {
                var error = "Failed to load members page " + newPage;

                if (data.data && data.data.message)
                    error = error + ": " + data.data.message;

                console.error(error);
                growl.showError(lmsg("projects.team.page_load_error"));
            });
        };
    }
}());


/***/ }),

/***/ "./js/directive/colourPicker.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.directive('claColourPicker', claColourPicker);

	function claColourPicker()
	{
		return {
			restrict: 'E',
			require: '^ngModel',
			templateUrl: '/intranet/projects/html/components/colour_picker.html',
			scope: {
				ngModel: '='
			},
			link: link
		};

		function link(scope, iElement, iAttrs, ctrl)
		{
			scope.id = iAttrs.id;
			scope.ctrl = ctrl;

			ctrl.$viewChangeListeners.push(function() {
				scope.$eval(iAttrs.ngChange);
			});
		}
	}
}());

/***/ }),

/***/ "./js/directive/datePicker.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.directive('claDatePicker', ['$timeout', claDatePicker]);

	function claDatePicker($timeout)
	{
		controller.$inject = ['$scope'];
		return {
			restrict: 'E',
			require: '^ngModel',
			templateUrl: '/intranet/projects/html/components/date_picker.html',
			scope: {
				ngModel: '=',
				control: '='
			},
			controller: controller,
			link: link
		};

		function controller($scope)
		{
            this.$onInit = function() {
                $scope.control.reset = function(){
                    angular.element('#' + $scope.id + '-inner').datetimepicker('clear');
                };
            };
		}

		function link(scope, iElement, iAttrs, ctrl)
		{
			scope.id = iAttrs.id;
			scope.name = iAttrs.name;
			scope.ctrl = ctrl;


			ctrl.$viewChangeListeners.push(function() {
				scope.$eval(iAttrs.ngChange);
			});

			angular.element('#' + scope.id + '-inner').on('blur', function(){
				element = $('#' + scope.id + '-inner');
				newval = element.val();
				ctrl.$setViewValue(newval);
			});

			$timeout(function()
			{
				date_picker_init(angular.element('#' + scope.id + '-inner'));
			}, 0);
		}
	}
}());


/***/ }),

/***/ "./js/directive/history_changes.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_changes.directive.html");

    angular.module(moduleName)
        .directive('historyChanges', historyChanges);

    function historyChanges()
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                changes: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.isSpecialUpdateType = function(type)
            {
                var specialTypes = [
                    'assignee',
                    'assignees',
                    'priority',
                    'status',
                    'list',
                    'is_private'
                ];
                return specialTypes.indexOf(type) > -1;
            };
        }
    }
}());


/***/ }),

/***/ "./js/directive/history_is_private.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_is_private.directive.html");

    angular.module(moduleName)
        .directive('historyIsPrivate', historyPrivate);

    function historyPrivate()
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                private: '='
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.getTaskPrivateName = function()
            {
                if (ctrl.private)
                    return lmsg('projects.history.type.private');

                return lmsg('projects.history.type.not_private');
            }
        }
    }
}());


/***/ }),

/***/ "./js/directive/history_list.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_list.directive.html");

    angular.module(moduleName)
        .directive('historyList', historyList);

    historyList.$inject = ['claProjectsHistoryService'];
    function historyList(service)
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                list: '<',
                listStr: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.getListName = function()
            {
                var name = service.getListName(parseInt(ctrl.list));

                if (name !== null)
                    return name;
                else
                    return ctrl.listStr;
            }
        }
    }
}());


/***/ }),

/***/ "./js/directive/history_priority.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_priority.directive.html");

    angular.module(moduleName)
        .directive('historyPriority', historyPriority);

    function historyPriority()
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                priority: '@'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.getPriorityName = function()
            {
                return priorityLookup[ctrl.priority];
            }
        }
    }

    var priorityLookup = {
        0: lmsg('projects.task.priority.none'),
        1: lmsg('projects.task.priority.low'),
        2: lmsg('projects.task.priority.medium'),
        3: lmsg('projects.task.priority.high')
    };
}());


/***/ }),

/***/ "./js/directive/history_status.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_status.directive.html");

    angular.module(moduleName)
        .directive('historyStatus', historyStatus);

    historyStatus.$inject = ['claProjectsHistoryService'];
    function historyStatus(service)
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                status: '<',
                statusStr: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.getStatusName = function()
            {
                var name = service.getStatusName(parseInt(ctrl.status));

                if (name !== null)
                    return name;
                else
                    return ctrl.statusStr;
            }
        }
    }
}());


/***/ }),

/***/ "./js/directive/history_user.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var moduleName = 'claromentis.project';
    var template = __webpack_require__("./html/directives/history_user.directive.html");

    angular.module(moduleName)
        .directive('historyUser', historyUser);

    function historyUser()
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                user: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.isUser = function()
            {
                return ((typeof ctrl.user === 'object') && (ctrl.user !== null) && (ctrl.user.id !== undefined) && (ctrl.user.id !== null));
            };
        }
    }
}());


/***/ }),

/***/ "./js/directive/nav_tabs.js":
/***/ (function(module, exports, __webpack_require__) {

var taskItemLoaded = taskItemLoaded || false;

(function()
{
    var template = __webpack_require__("./html/directives/project_nav_tabs.directive.html");
	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	if (!taskItemLoaded)
	{
		module.directive('projectNavTabs', ['$timeout', 'claProjectDetailsService', function ($timeout, service) {
            return {
                restrict: 'E',
                templateUrl: template,
                bindToController: true,
                controllerAs: 'ctrl',
                controller: function() {
                    var ctrl = this

                    ctrl.tab = 'overviewTab';
                    ctrl.tabCounts = {};
                },
                link: function($scope, elements, attr, ctrl) {
                    ctrl.project_perms = project_perms;

                    ctrl.tabCounts = service.tabCounts;
                    service.registerCallback(function(locationData) {
                        ctrl.tab = locationData.tab;
                        ctrl.tabCounts.taskCount = locationData.counts.taskCount;
                        ctrl.tabCounts.totalTaskCount = locationData.counts.totalTaskCount;
                        ctrl.tabCounts.teamCount = locationData.counts.teamCount;
                        ctrl.tabCounts.topicCount = locationData.counts.topicCount;
                        ctrl.tabCounts.fileCount = locationData.counts.fileCount;

                        $timeout(function() {
                            $scope.$digest();
                        }, 0);
                    });

                    // Deal with page load
                    if (window.location.hash !== '') {
                        service.setLocation(window.location.hash.slice(2));
                    }

                    // Store the basic URL that we should check hash changes for
                    var thisPage = window.location.host + window.location.pathName;

                    // Deal with subsequent URL changes
                    window.addEventListener('hashchange', function() {
                        // Do nothing if the page itself is being changed
                        if (thisPage !== window.location.host + window.location.pathName)
                            return;

                        service.setLocation(window.location.hash.slice(2));
                    })
                }
            };
        }]);
	}
}());


/***/ }),

/***/ "./js/directive/topics_table.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    let moduleName = 'claromentis.project';
    let template = __webpack_require__("./html/directives/topics_table.directive.html");

    angular.module(moduleName)
        .directive('topicsTable', topicsTable);

    topicsTable.$inject = ['$timeout', '$http', 'topicsPreloadFactory'];
    function topicsTable($timeout, $http, preload)
    {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {},
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller,
            link: link
        };

        function controller()
        {
            let ctrl = this;

            this.$onInit = function() {
                ctrl.columns = preload['columns'];
                ctrl.cellTemplates = preload['cell_templates'];
                ctrl.column_defs = preload['column_defs'];
                ctrl.project_id = preload['project_id'];

                // Datatables expects the templates to be in DOM so wait until after the digest ends to flag it as ready to go
                $timeout(function() {
                    ctrl.templates_loaded = true;
                }, 0);
            }

            ctrl.templates_loaded = false;
        }

        function link (scope, element, attrs, controller)
        {
            angular.element(element).on('click', ".pin-toggle", function(event){
                event.preventDefault();

                let $this = angular.element(this);
                let pinUrl = $this.attr('href');
                let pinned = 0;
                if ($this.hasClass('pin-topic'))
                    pinned = 1;

                $http.put(
                    pinUrl,
                    {
                        pinned: pinned
                    }
                ).then(function() {
                    $("#topics-datatable").DataTable().ajax.reload();
                }, function(){});
            });
        }
    }
}());


/***/ }),

/***/ "./js/lib/ng-sortable.js":
/***/ (function(module, exports) {

/*
 The MIT License (MIT)

 Copyright (c) 2014 Muhammed Ashik

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */

/*jshint indent: 2 */
/*global angular: false */

(function () {
  'use strict';
  angular.module('ui.sortable', [])
    .constant('sortableConfig', {
      itemClass: 'as-sortable-item',
      handleClass: 'as-sortable-item-handle',
      placeHolderClass: 'as-sortable-placeholder',
      dragClass: 'as-sortable-drag',
      hiddenClass: 'as-sortable-hidden',
      dragging: 'as-sortable-dragging'
    });
}());

/*jshint indent: 2 */
/*global angular: false */

(function () {
  'use strict';

  var mainModule = angular.module('ui.sortable');

  /**
   * Helper factory for sortable.
   */
  mainModule.factory('$helper', ['$document', '$window',
    function ($document, $window) {
      return {

        /**
         * Get the height of an element.
         *
         * @param {Object} element Angular element.
         * @returns {String} Height
         */
        height: function (element) {
          return element[0].getBoundingClientRect().height;
        },

        /**
         * Get the width of an element.
         *
         * @param {Object} element Angular element.
         * @returns {String} Width
         */
        width: function (element) {
          return element[0].getBoundingClientRect().width;
        },

        /**
         * Get the offset values of an element.
         *
         * @param {Object} element Angular element.
         * @param {Object} [scrollableContainer] Scrollable container object for calculating relative top & left (optional, defaults to Document)
         * @returns {Object} Object with properties width, height, top and left
         */
        offset: function (element, scrollableContainer) {
          var boundingClientRect = element[0].getBoundingClientRect();
          if (!scrollableContainer) {
            scrollableContainer = $document[0].documentElement;
          }

          return {
            width: boundingClientRect.width || element.prop('offsetWidth'),
            height: boundingClientRect.height || element.prop('offsetHeight'),
            top: boundingClientRect.top + ($window.pageYOffset || scrollableContainer.scrollTop - scrollableContainer.offsetTop),
            left: boundingClientRect.left + ($window.pageXOffset || scrollableContainer.scrollLeft - scrollableContainer.offsetLeft)
          };
        },

        /**
         * get the event object for touch.
         *
         * @param  {Object} event the touch event
         * @return {Object} the touch event object.
         */
        eventObj: function (event) {
          var obj = event;
          if (event.targetTouches !== undefined) {
            obj = event.targetTouches.item(0);
          } else if (event.originalEvent !== undefined && event.originalEvent.targetTouches !== undefined) {
            obj = event.originalEvent.targetTouches.item(0);
          }
          return obj;
        },

        /**
         * Checks whether the touch is valid and multiple.
         *
         * @param event the event object.
         * @returns {boolean} true if touch is multiple.
         */
        isTouchInvalid: function (event) {

          var touchInvalid = false;
          if (event.touches !== undefined && event.touches.length > 1) {
            touchInvalid = true;
          } else if (event.originalEvent !== undefined &&
            event.originalEvent.touches !== undefined && event.originalEvent.touches.length > 1) {
            touchInvalid = true;
          }
          return touchInvalid;
        },

        /**
         * Get the start position of the target element according to the provided event properties.
         *
         * @param {Object} event Event
         * @param {Object} target Target element
         * @param {Object} [scrollableContainer] (optional) Scrollable container object
         * @returns {Object} Object with properties offsetX, offsetY.
         */
        positionStarted: function (event, target, scrollableContainer) {
          var pos = {};
          pos.offsetX = event.pageX - this.offset(target, scrollableContainer).left;
          pos.offsetY = event.pageY - this.offset(target, scrollableContainer).top;
          pos.startX = pos.lastX = event.pageX;
          pos.startY = pos.lastY = event.pageY;
          pos.nowX = pos.nowY = pos.distX = pos.distY = pos.dirAx = 0;
          pos.dirX = pos.dirY = pos.lastDirX = pos.lastDirY = pos.distAxX = pos.distAxY = 0;
          return pos;
        },

        /**
         * Calculates the event position and sets the direction
         * properties.
         *
         * @param pos the current position of the element.
         * @param event the move event.
         */
        calculatePosition: function (pos, event) {
          // mouse position last events
          pos.lastX = pos.nowX;
          pos.lastY = pos.nowY;

          // mouse position this events
          pos.nowX = event.pageX;
          pos.nowY = event.pageY;

          // distance mouse moved between events
          pos.distX = pos.nowX - pos.lastX;
          pos.distY = pos.nowY - pos.lastY;

          // direction mouse was moving
          pos.lastDirX = pos.dirX;
          pos.lastDirY = pos.dirY;

          // direction mouse is now moving (on both axis)
          pos.dirX = pos.distX === 0 ? 0 : pos.distX > 0 ? 1 : -1;
          pos.dirY = pos.distY === 0 ? 0 : pos.distY > 0 ? 1 : -1;

          // axis mouse is now moving on
          var newAx = Math.abs(pos.distX) > Math.abs(pos.distY) ? 1 : 0;

          // calc distance moved on this axis (and direction)
          if (pos.dirAx !== newAx) {
            pos.distAxX = 0;
            pos.distAxY = 0;
          } else {
            pos.distAxX += Math.abs(pos.distX);
            if (pos.dirX !== 0 && pos.dirX !== pos.lastDirX) {
              pos.distAxX = 0;
            }

            pos.distAxY += Math.abs(pos.distY);
            if (pos.dirY !== 0 && pos.dirY !== pos.lastDirY) {
              pos.distAxY = 0;
            }
          }
          pos.dirAx = newAx;
        },

        /**
         * Move the position by applying style.
         *
         * @param event the event object
         * @param element - the dom element
         * @param pos - current position
         * @param container - the bounding container.
         * @param containerPositioning - absolute or relative positioning.
         * @param {Object} [scrollableContainer] (optional) Scrollable container object
         */
        movePosition: function (event, element, pos, container, containerPositioning, scrollableContainer) {
          var bounds;
          var useRelative = (containerPositioning === 'relative');

          element.x = event.pageX - pos.offsetX;
          element.y = event.pageY - pos.offsetY;

          if (container) {
            bounds = this.offset(container, scrollableContainer);

            if (useRelative) {
              // reduce positioning by bounds
              element.x -= bounds.left;
              element.y -= bounds.top;

              // reset bounds
              bounds.left = 0;
              bounds.top = 0;
            }

            if (element.x < bounds.left) {
              element.x = bounds.left;
            } else if (element.x >= bounds.width + bounds.left - this.offset(element).width) {
              element.x = bounds.width + bounds.left - this.offset(element).width;
            }
            if (element.y < bounds.top) {
              element.y = bounds.top;
            } else if (element.y >= bounds.height + bounds.top - this.offset(element).height) {
              element.y = bounds.height + bounds.top - this.offset(element).height;
            }
          }

          element.css({
            'left': element.x + 'px',
            'top': element.y + 'px'
          });

          this.calculatePosition(pos, event);
        },

        /**
         * The drag item info and functions.
         * retains the item info before and after move.
         * holds source item and target scope.
         *
         * @param item - the drag item
         * @returns {{index: *, parent: *, source: *,
                 *          sourceInfo: {index: *, itemScope: (*|.dragItem.sourceInfo.itemScope|$scope.itemScope|itemScope), sortableScope: *},
                 *         moveTo: moveTo, isSameParent: isSameParent, isOrderChanged: isOrderChanged, eventArgs: eventArgs, apply: apply}}
         */
        dragItem: function (item) {

          return {
            index: item.itemScope.index(),
            parent: item.sortableScope,
            source: item,
            sourceInfo: {
              index: item.itemScope.index(),
              itemScope: item.itemScope,
              sortableScope: item.sortableScope
            },
            moveTo: function (parent, index) { // Move the item to a new position
              this.parent = parent;
              //If source Item is in the same Parent.
              if (this.isSameParent() && this.source.itemScope.index() < index) { // and target after
                index = index - 1;
              }
              this.index = index;
            },
            isSameParent: function () {
              return this.parent.element === this.sourceInfo.sortableScope.element;
            },
            isOrderChanged: function () {
              return this.index !== this.sourceInfo.index;
            },
            eventArgs: function () {
              return {
                source: this.sourceInfo,
                dest: {
                  index: this.index,
                  sortableScope: this.parent
                }
              };
            },
            apply: function () {
              var item = this.sourceInfo.sortableScope.removeItem(this.sourceInfo.index); // Remove from source.
              this.parent.insertItem(this.index, item); // Insert in to destination.
            }
          };
        },

        /**
         * Check the drag is not allowed for the element.
         *
         * @param element - the element to check
         * @returns {boolean} - true if drag is not allowed.
         */
        noDrag: function (element) {
          return element.attr('no-drag') !== undefined || element.attr('data-no-drag') !== undefined;
        }
      };
    }
  ]);

}());
/*jshint undef: false, unused: false, indent: 2*/
/*global angular: false */

(function () {

  'use strict';
  var mainModule = angular.module('ui.sortable');

  /**
   * Controller for Sortable.
   * @param $scope - the sortable scope.
   */
  mainModule.controller('ui.sortable.sortableController', ['$scope', function ($scope) {

    this.scope = $scope;

    $scope.modelValue = null; // sortable list.
    $scope.callbacks = null;
    $scope.type = 'sortable';
    $scope.options = {};
    $scope.isDisabled = false;

    /**
     * Inserts the item in to the sortable list.
     *
     * @param index - the item index.
     * @param itemData - the item model data.
     */
    $scope.insertItem = function (index, itemData) {
      $scope.modelValue.splice(index, 0, itemData);
    };

    /**
     * Removes the item from the sortable list.
     *
     * @param index - index to be removed.
     * @returns {*} - removed item.
     */
    $scope.removeItem = function (index) {
      var removedItem = null;
      if (index > -1) {
        removedItem = $scope.modelValue.splice(index, 1)[0];
      }
      return removedItem;
    };

    /**
     * Checks whether the sortable list is empty.
     *
     * @returns {null|*|$scope.modelValue|boolean}
     */
    $scope.isEmpty = function () {
      return ($scope.modelValue && $scope.modelValue.length === 0);
    };

    /**
     * Wrapper for the accept callback delegates to callback.
     *
     * @param sourceItemHandleScope - drag item handle scope.
     * @param destScope - sortable target scope.
     * @param destItemScope - sortable destination item scope.
     * @returns {*|boolean} - true if drop is allowed for the drag item in drop target.
     */
    $scope.accept = function (sourceItemHandleScope, destScope, destItemScope) {
      return $scope.callbacks.accept(sourceItemHandleScope, destScope, destItemScope);
    };

  }]);

  /**
   * Sortable directive - defines callbacks.
   * Parent directive for draggable and sortable items.
   * Sets modelValue, callbacks, element in scope.
   */
  mainModule.directive('asSortable',
    function ($parse) {
      return {
        require: 'ngModel', // get a hold of NgModelController
        restrict: 'A',
        scope: { asSortable: '&' },
        controller: 'ui.sortable.sortableController',
        link: function (scope, element, attrs, ngModelController) {

          var ngModel, callbacks;

          ngModel = ngModelController;

          if (!ngModel) {
            return; // do nothing if no ng-model
          }

          // Set the model value in to scope.
          ngModel.$render = function () {
            //set an empty array, in case if none is provided.
            if (!ngModel.$modelValue || !angular.isArray(ngModel.$modelValue)) {
              ngModel.$setViewValue([]);
            }
            scope.modelValue = ngModel.$modelValue;
          };
          //set the element in scope to be accessed by its sub scope.
          scope.element = element;

          callbacks = {accept: null, orderChanged: null, itemMoved: null, dragStart: null, dragMove:null, dragCancel: null, dragEnd: null};

          /**
           * Invoked to decide whether to allow drop.
           *
           * @param sourceItemHandleScope - the drag item handle scope.
           * @param destSortableScope - the drop target sortable scope.
           * @param destItemScope - the drop target item scope.
           * @returns {boolean} - true if allowed for drop.
           */
          callbacks.accept = function (sourceItemHandleScope, destSortableScope, destItemScope) {
            return true;
          };

          /**
           * Invoked when order of a drag item is changed.
           *
           * @param event - the event object.
           */
          callbacks.orderChanged = function (event) {
          };

          /**
           * Invoked when the item is moved to other sortable.
           *
           * @param event - the event object.
           */
          callbacks.itemMoved = function (event) {
          };

          /**
           * Invoked when the drag started successfully.
           *
           * @param event - the event object.
           */
          callbacks.dragStart = function (event) {
          };

          /**
           * Invoked when the drag started successfully.
           *
           * @param event - the event object.
          */
          callbacks.dragMove = function (event) {
          };

          /**
           * Invoked when the drag cancelled.
           *
           * @param event - the event object.
           */
          callbacks.dragCancel = function (event) {
          };

          /**
           * Invoked when the drag stopped.
           *
           * @param event - the event object.
           */
          callbacks.dragEnd = function (event) {
          };

          //Set the sortOptions callbacks else set it to default.
          scope.$watch(scope.asSortable, function (newVal, oldVal) {
            angular.forEach(newVal, function (value, key) {
              if (callbacks[key]) {
                if (typeof value === 'function') {
                  callbacks[key] = value;
                }
              } else {
                scope.options[key] = value;
              }
            });
            scope.callbacks = callbacks;
          }, true);

          // Set isDisabled if attr is set, if undefined isDisabled = false
          if (angular.isDefined(attrs.isDisabled)) {
            scope.$watch(attrs.isDisabled, function (newVal, oldVal) {
              if (!angular.isUndefined(newVal)) {
                scope.isDisabled = newVal;
              }
            }, true);
          }
        }
      };
    });

}());

/*jshint indent: 2 */
/*global angular: false */

(function () {

  'use strict';
  var mainModule = angular.module('ui.sortable');

  /**
   * Controller for sortableItemHandle
   *
   * @param $scope - item handle scope.
   */
  mainModule.controller('ui.sortable.sortableItemHandleController', ['$scope', function ($scope) {

    this.scope = $scope;

    $scope.itemScope = null;
    $scope.type = 'handle';
  }]);

  /**
   * Directive for sortable item handle.
   */
  mainModule.directive('asSortableItemHandle', ['sortableConfig', '$helper', '$window', '$document',
    function (sortableConfig, $helper, $window, $document) {
      return {
        require: '^asSortableItem',
        scope: {},
        restrict: 'A',
        controller: 'ui.sortable.sortableItemHandleController',
        link: function (scope, element, attrs, itemController) {

          var dragElement, //drag item element.
            placeHolder, //place holder class element.
            placeElement,//hidden place element.
            itemPosition, //drag item element position.
            dragItemInfo, //drag item data.
            containment,//the drag container.
            containerPositioning, // absolute or relative positioning.
            dragListen,// drag listen event.
            scrollableContainer, //the scrollable container
            dragStart,// drag start event.
            dragMove,//drag move event.
            dragEnd,//drag end event.
            dragCancel,//drag cancel event.
            isDraggable,//is element draggable.
            isDragBefore,//is element moved up direction.
            isPlaceHolderPresent,//is placeholder present.
            bindDrag,//bind drag events.
            unbindDrag,//unbind drag events.
            bindEvents,//bind the drag events.
            unBindEvents,//unbind the drag events.
            hasTouch,// has touch support.
            dragHandled, //drag handled.
            createPlaceholder,
            isDisabled = false; // drag enabled

          hasTouch = $window.hasOwnProperty('ontouchstart');

          if (sortableConfig.handleClass) {
            element.addClass(sortableConfig.handleClass);
          }

          scope.itemScope = itemController.scope;
	      scope.sortableScope = scope.itemScope.sortableScope;

          scope.$watch('sortableScope.isDisabled', function (newVal) {
            if (isDisabled !== newVal) {
              isDisabled = newVal;
              if (isDisabled) {
                unbindDrag();
              } else {
                bindDrag();
              }
            }
          });

          createPlaceholder = function(itemScope) {
            if(typeof scope.sortableScope.options.placeholder === 'function') {
              return angular.element(scope.sortableScope.options.placeholder(itemScope));
            } else if(typeof scope.sortableScope.options.placeholder === 'string') {
              return angular.element(scope.sortableScope.options.placeholder);
            } else {
              return angular.element($document[0].createElement(itemScope.element.prop('tagName')));
            }
          };

          /**
           * Listens for a 10px movement before
           * dragStart is called to allow for
           * a click event on the element.
           *
           * @param event - the event object.
           */
          dragListen = function (event) {

            var unbindMoveListen = function () {
              angular.element($document).unbind('mousemove', moveListen);
              angular.element($document).unbind('touchmove', moveListen);
              element.unbind('mouseup', unbindMoveListen);
              element.unbind('touchend', unbindMoveListen);
              element.unbind('touchcancel', unbindMoveListen);
            };

            var startPosition;
            var moveListen = function (e) {
              e.preventDefault();
              var eventObj = $helper.eventObj(e);
              if (!startPosition) {
                startPosition = { clientX: eventObj.clientX, clientY: eventObj.clientY };
              }
              if (Math.abs(eventObj.clientX - startPosition.clientX) + Math.abs(eventObj.clientY - startPosition.clientY) > 10) {
                unbindMoveListen();
                dragStart(event);
              }
            };

            angular.element($document).bind('mousemove', moveListen);
            angular.element($document).bind('touchmove', moveListen);
            element.bind('mouseup', unbindMoveListen);
            element.bind('touchend', unbindMoveListen);
            element.bind('touchcancel', unbindMoveListen);
          };

          /**
           * Triggered when drag event starts.
           *
           * @param event the event object.
           */
          dragStart = function (event) {

            var eventObj, tagName;

            if (!hasTouch && (event.button === 2 || event.which === 3)) {
              // disable right click
              return;
            }
            if (hasTouch && $helper.isTouchInvalid(event)) {
              return;
            }
            if (dragHandled || !isDraggable(event)) {
              // event has already fired in other scope.
              return;
            }
            // Set the flag to prevent other items from inheriting the drag event
            dragHandled = true;
            event.preventDefault();
            eventObj = $helper.eventObj(event);

	        // sortableScope may not be setup yet
	        if (scope.sortableScope === null)
	        {
		        scope.sortableScope = scope.itemScope.sortableScope;
	        }

            // (optional) Scrollable container as reference for top & left offset calculations, defaults to Document
            scrollableContainer = angular.element($document[0].querySelector(scope.sortableScope.options.scrollableContainer)).length > 0 ?
              $document[0].querySelector(scope.sortableScope.options.scrollableContainer) : $document[0].documentElement;

            containment = angular.element($document[0].querySelector(scope.sortableScope.options.containment)).length > 0 ?
              angular.element($document[0].querySelector(scope.sortableScope.options.containment)) : angular.element($document[0].body);
            //capture mouse move on containment.
            containment.css('cursor', 'move');
            containment.addClass('as-sortable-un-selectable');

            // container positioning
            containerPositioning = scope.sortableScope.options.containerPositioning || 'absolute';

            dragItemInfo = $helper.dragItem(scope);
            tagName = scope.itemScope.element.prop('tagName');

            dragElement = angular.element($document[0].createElement(scope.sortableScope.element.prop('tagName')))
              .addClass(scope.sortableScope.element.attr('class')).addClass(sortableConfig.dragClass);
            dragElement.css('width', $helper.width(scope.itemScope.element) + 'px');
            dragElement.css('height', $helper.height(scope.itemScope.element) + 'px');

            placeHolder = createPlaceholder(scope.itemScope)
              .addClass(sortableConfig.placeHolderClass).addClass(scope.sortableScope.options.additionalPlaceholderClass);
            placeHolder.css('width', $helper.width(scope.itemScope.element) + 'px');
            placeHolder.css('height', $helper.height(scope.itemScope.element) + 'px');

            placeElement = angular.element($document[0].createElement(tagName));
            if (sortableConfig.hiddenClass) {
              placeElement.addClass(sortableConfig.hiddenClass);
            }

            itemPosition = $helper.positionStarted(eventObj, scope.itemScope.element, scrollableContainer);
            //fill the immediate vacuum.
            scope.itemScope.element.after(placeHolder);
            //hidden place element in original position.
            scope.itemScope.element.after(placeElement);
            dragElement.append(scope.itemScope.element);

            containment.append(dragElement);
            $helper.movePosition(eventObj, dragElement, itemPosition, containment, containerPositioning, scrollableContainer);

            scope.sortableScope.$apply(function () {
              scope.sortableScope.callbacks.dragStart(dragItemInfo.eventArgs());
            });
            bindEvents();
          };

          /**
           * Allow Drag if it is a proper item-handle element.
           *
           * @param event - the event object.
           * @return boolean - true if element is draggable.
           */
          isDraggable = function (event) {

            var elementClicked, sourceScope, isDraggable;

            elementClicked = angular.element(event.target);
            sourceScope = scope;//elementClicked.scope();

            // look for the handle on the current scope or parent scopes
            isDraggable = false;
            while (!isDraggable && sourceScope !== undefined) {
              if (sourceScope.type && sourceScope.type === 'handle') {
                isDraggable = true;
              } else {
                sourceScope = sourceScope.$parent;
              }
            }

            //If a 'no-drag' element inside item-handle if any.
            while (isDraggable && elementClicked[0] !== element[0]) {
              if ($helper.noDrag(elementClicked)) {
                isDraggable = false;
              }
              elementClicked = elementClicked.parent();
            }
            return isDraggable;
          };

          /**
           * Inserts the placeHolder in to the targetScope.
           *
           * @param targetElement the target element
           * @param targetScope the target scope
           */
          function insertBefore(targetElement, targetScope) {
            targetElement[0].parentNode.insertBefore(placeHolder[0], targetElement[0]);
            dragItemInfo.moveTo(targetScope.sortableScope, targetScope.index());
          }

          /**
           * Inserts the placeHolder next to the targetScope.
           *
           * @param targetElement the target element
           * @param targetScope the target scope
           */
          function insertAfter(targetElement, targetScope) {
            targetElement.after(placeHolder);
            dragItemInfo.moveTo(targetScope.sortableScope, targetScope.index() + 1);
          }

			/** Find the closest ancestor that is a sortable element **/
		  function findDropScope(targetElement)
		  {
			  var iterations = 0;
			  var targetScope = targetElement.isolateScope();
			  while ((typeof targetScope === 'undefined') ||
			  		 (typeof targetScope.type === 'undefined'))
			  {
				  targetElement = angular.element(targetElement).parent();
				  targetScope = targetElement.isolateScope();

				  // Exit if nothing found (may be nested too deep, or the mouse isn't over the sortable container, or even not on the page anymore)
				  iterations++;
				  if (iterations > 32)
				  	return null;
			  }

			  return targetScope;
		  }

          /**
           * Triggered when drag is moving.
           *
           * @param event - the event object.
           */
          dragMove = function (event) {

            var eventObj, targetX, targetY, targetScope, targetElement;

            if (hasTouch && $helper.isTouchInvalid(event)) {
              return;
            }
            // Ignore event if not handled
            if (!dragHandled) {
              return;
            }
            if (dragElement) {

              event.preventDefault();

              eventObj = $helper.eventObj(event);
              scope.sortableScope.$apply(function () {
                scope.sortableScope.callbacks.dragMove(itemPosition, containment);
              });
              $helper.movePosition(eventObj, dragElement, itemPosition, containment, containerPositioning, scrollableContainer);

              targetX = eventObj.pageX - $document[0].documentElement.scrollLeft;
              targetY = eventObj.pageY - ($window.pageYOffset || $document[0].documentElement.scrollTop);

              //IE fixes: hide show element, call element from point twice to return pick correct element.
              dragElement.addClass(sortableConfig.hiddenClass);
              $document[0].elementFromPoint(targetX, targetY);
              targetElement = angular.element($document[0].elementFromPoint(targetX, targetY));

              dragElement.removeClass(sortableConfig.hiddenClass);

              //Set Class as dragging starts
              dragElement.addClass(sortableConfig.dragging);

			  // Find the nearest sortable parent container if one exists
              targetScope = findDropScope(targetElement);

              if (!targetScope || !targetScope.type) {
                return;
              }
              if (targetScope.type === 'handle') {
                targetScope = targetScope.itemScope;
              }
              if (targetScope.type !== 'item' && targetScope.type !== 'sortable') {
                return;
              }

              if (targetScope.type === 'item') {
                targetElement = targetScope.element;
                if (targetScope.sortableScope.accept(scope, targetScope.sortableScope, targetScope)) {
                  if (itemPosition.dirAx && //move horizontal
                    scope.itemScope.sortableScope.$id === targetScope.sortableScope.$id) { //move same column
                    itemPosition.distAxX = 0;
                    if (itemPosition.distX < 0) {//move left
                      insertBefore(targetElement, targetScope);
                    } else if (itemPosition.distX > 0) {//move right
                      insertAfter(targetElement, targetScope);
                    }
                  } else { //move vertical
                    if (isDragBefore(eventObj, targetElement)) {//move up
                      insertBefore(targetElement, targetScope);
                    } else {//move bottom
                      insertAfter(targetElement, targetScope);
                    }
                  }
                }
              }
              if (targetScope.type === 'sortable') {//sortable scope.
                if (targetScope.accept(scope, targetScope) &&
                  targetElement[0].parentNode !== targetScope.element[0]) {
                  //moving over sortable bucket. not over item.
                  if (!isPlaceHolderPresent(targetElement)) {
                    //append to bottom.
                    targetElement[0].appendChild(placeHolder[0]);
                    dragItemInfo.moveTo(targetScope, targetScope.modelValue.length);
                  }
                }
              }
            }
          };

          /**
           * Check there is no place holder placed by itemScope.
           * @param targetElement the target element to check with.
           * @returns {*} true if place holder present.
           */
          isPlaceHolderPresent = function (targetElement) {
            var itemElements, hasPlaceHolder, i;

            itemElements = targetElement.children();
            for (i = 0; i < itemElements.length; i += 1) {
              if (angular.element(itemElements[i]).hasClass(sortableConfig.placeHolderClass)) {
                hasPlaceHolder = true;
                break;
              }
            }
            return hasPlaceHolder;
          };


          /**
           * Determines whether the item is dragged upwards.
           *
           * @param eventObj - the event object.
           * @param targetElement - the target element.
           * @returns {boolean} - true if moving upwards.
           */
          isDragBefore = function (eventObj, targetElement) {
            var dragBefore, targetOffset;

            dragBefore = false;
            // check it's new position
            targetOffset = $helper.offset(targetElement);
            if ($helper.offset(placeHolder).top > targetOffset.top) { // the move direction is up?
              dragBefore = $helper.offset(dragElement).top < targetOffset.top + $helper.height(targetElement) / 2;
            } else {
              dragBefore = eventObj.pageY < targetOffset.top;
            }
            return dragBefore;
          };

          /**
           * Rollback the drag data changes.
           */

          function rollbackDragChanges() {
            placeElement.replaceWith(scope.itemScope.element);
            placeHolder.remove();
            dragElement.remove();
            dragElement = null;
            dragHandled = false;
            containment.css('cursor', '');
            containment.removeClass('as-sortable-un-selectable');
          }

          /**
           * triggered while drag ends.
           *
           * @param event - the event object.
           */
          dragEnd = function (event) {
            // Ignore event if not handled
            if (!dragHandled) {
              return;
            }
            event.preventDefault();
            if (dragElement) {
              //rollback all the changes.
              rollbackDragChanges();
              // update model data
              dragItemInfo.apply();
              scope.sortableScope.$apply(function () {
                if (dragItemInfo.isSameParent()) {
                  if (dragItemInfo.isOrderChanged()) {
                    scope.sortableScope.callbacks.orderChanged(dragItemInfo.eventArgs());
                  }
                } else {
                  scope.sortableScope.callbacks.itemMoved(dragItemInfo.eventArgs());
                }
              });
              scope.sortableScope.$apply(function () {
                scope.sortableScope.callbacks.dragEnd(dragItemInfo.eventArgs());
              });
              dragItemInfo = null;
            }
            unBindEvents();
          };

          /**
           * triggered while drag is cancelled.
           *
           * @param event - the event object.
           */
          dragCancel = function (event) {
            // Ignore event if not handled
            if (!dragHandled) {
              return;
            }
            event.preventDefault();

            if (dragElement) {
              //rollback all the changes.
              rollbackDragChanges();
              scope.sortableScope.$apply(function () {
                scope.sortableScope.callbacks.dragCancel(dragItemInfo.eventArgs());
              });
              dragItemInfo = null;
            }
            unBindEvents();
          };

          /**
           * Binds the drag start events.
           */
          bindDrag = function () {
            element.bind('touchstart', dragListen);
            element.bind('mousedown', dragListen);
          };

          /**
           * Unbinds the drag start events.
           */
          unbindDrag = function () {
            element.unbind('touchstart', dragListen);
            element.unbind('mousedown', dragListen);
          };

          //bind drag start events.
          bindDrag();

          //Cancel drag on escape press.
          angular.element($document[0].body).bind('keydown', function (event) {
            if (event.keyCode === 27) {
              dragCancel(event);
            }
          });

          /**
           * Binds the events based on the actions.
           */
          bindEvents = function () {
            angular.element($document).bind('touchmove', dragMove);
            angular.element($document).bind('touchend', dragEnd);
            angular.element($document).bind('touchcancel', dragCancel);
            angular.element($document).bind('mousemove', dragMove);
            angular.element($document).bind('mouseup', dragEnd);
          };

          /**
           * Un binds the events for drag support.
           */
          unBindEvents = function () {
            angular.element($document).unbind('touchend', dragEnd);
            angular.element($document).unbind('touchcancel', dragCancel);
            angular.element($document).unbind('touchmove', dragMove);
            angular.element($document).unbind('mouseup', dragEnd);
            angular.element($document).unbind('mousemove', dragMove);
          };
        }
      };
    }]);
}());

/*jshint indent: 2 */
/*global angular: false */

(function () {

  'use strict';
  var mainModule = angular.module('ui.sortable');

  /**
   * Controller for sortable item.
   *
   * @param $scope - drag item scope
   */
  mainModule.controller('ui.sortable.sortableItemController', ['$scope', function ($scope) {

    this.scope = $scope;

    $scope.sortableScope = null;
    $scope.modelValue = null; // sortable item.
    $scope.type = 'item';

    /**
     * returns the index of the drag item from the sortable list.
     *
     * @returns {*} - index value.
     */
    $scope.index = function () {
      return $scope.$parent.$index;
    };

    /**
     * Returns the item model data.
     *
     * @returns {*} - item model value.
     */
    $scope.itemData = function () {
      return $scope.sortableScope.modelValue[$scope.$index];
    };

  }]);

  /**
   * sortableItem directive.
   */
  mainModule.directive('asSortableItem', ['sortableConfig',
    function (sortableConfig) {
      return {
        require: ['^asSortable', '?ngModel'],
        restrict: 'A',
	    scope: {},
        controller: 'ui.sortable.sortableItemController',
        link: function (scope, element, attrs, ctrl) {
          var sortableController = ctrl[0];
          var ngModelController = ctrl[1];
          if (sortableConfig.itemClass) {
            element.addClass(sortableConfig.itemClass);
          }
          scope.sortableScope = sortableController.scope;
          if (ngModelController) {
            ngModelController.$render = function () {
              scope.modelValue = ngModelController.$modelValue;
            };
          } else {
		    scope.modelValue = sortableController.scope.modelValue[scope.$index];
          }
          scope.element = element;
        }
      };
    }]);

}());


/***/ }),

/***/ "./js/model/projectTaskModel.js":
/***/ (function(module, exports) {

(function () {
    var moduleName = 'claromentis.project.tasks_util';
    var module;

    try {
        module = angular.module(moduleName);
    } catch (err) {
        // named module does not exist, so create one
        module = angular.module(moduleName, []);
    }

    module
        .factory('Task', ['$http', taskModelFactory]);

	function taskModelFactory($http)
	{
		var Task = function()
		{
		};

        Task.prototype =
            {
                // Constant to mean 'unsorted' at the end of the list
                DISPLAY_ORDER_NONE: 1e6,
                STATUS_TODO: -1,
                STATUS_COMPLETED: -2,

                restUrl: '/api/projects/task/',

                id: 0,
                project_id: 0,
                title: '',
                description: '',
                due_date: null,
                due_date_formatted: '',
                section_id: 0,
                is_private: false,
                created_by:
                {
                    id: null,
                    name: '',
                    profile_url: '#'
                },
                assignees: [],
                priority: '0',
                is_complete: false,
                date_completed: null,
                duration: '',
                duration_units: '',
                comments_count: 0,
                has_file_storage: false,
                orders: {
                    task: this.DISPLAY_ORDER_NONE,
                    board: this.DISPLAY_ORDER_NONE,
                    board_no_list: this.DISPLAY_ORDER_NONE,
                    my_tasks: this.DISPLAY_ORDER_NONE
                },
                status_id: 0,
                status_list: [],
                source: null,

			    control: {}, // datepicker

                // Using is_complete directly for list visibility and checkbox model causes issues with click events (the model is updated, then visibility assessed and if not visible, the ng-click callback is never run).
                // So use this second copy for visibility that gets updated when the server responds.
                is_complete_server: true,

                tasks: [],
                new_task: new Task(),

			loadFromArray: function(data)
			{
				this.id = data.id;
				this.project_id = data.project_id;
				this.title = data.title;
                this.description = data.description;
                this.due_date = data.due_date;
				this.due_date_formatted = data.due_date_formatted;
				this.due_date_type = data.due_date_type;
                this.section_id = data.section_id;
                this.section_name = data.section_name;
                this.is_private = data.is_private;
				this.created_by = data.created_by;
				this.priority = parseInt(data.priority);
				this.assignees = data.assignees;
				this.is_complete = data.is_complete;
				this.is_complete_server = data.is_complete;
                this.date_completed = data.date_completed === null ? null : parseInt(data.date_completed);
				this.duration = data.duration == null ? '' : data.duration;
				this.duration_units = data.duration_units;
				this.date_created = data.date_created;
				this.comments_count = data.comments_count;
				this.perms = data.perms;
				if (data.source !== undefined)
                {
					this.source = {
						name: data.source.name,
						link: data.source.link
					};
				}
				this.has_file_storage = data.has_file_storage;

				this.orders = {
					list: data.orders.list,
					board: data.orders.board,
					board_no_list: data.orders.board_no_list,
					my_tasks: data.orders.my_tasks
				};

				this.status_id = data.status_id;
				this.status_list = data.status_list;
			},
			add: function(successCallback, errorCallback)
			{
				var data =
				{
                    project_id: this.project_id,
					title: this.title.length <= 255 ? this.title : this.title.substr(0, 255),
                    description: this.description,
					due_date: this.due_date,
					section_id: this.section_id,
					is_private: this.is_private,
                    assignees: this.assignees,
					priority: this.priority,
					duration: this.duration,
					is_complete: this.is_complete
				};
				$http(
					{
						method:'POST',
						url: this.restUrl + 'add',
						data: data,
						task: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
					{
						var task = response.config.task;
						task.id = response.data.id;
						task.project_id = response.data.project_id;
						task.title = response.data.title;
						task.description = response.data.description;
						task.due_date = response.data.due_date;
						task.due_date_formatted = response.data.due_date_formatted;
						task.due_date_type = response.data.due_date_type;
						task.section_id = response.data.section_id;
						task.is_private = response.data.is_private;
						task.created_by = response.data.created_by;
						task.assignees = response.data.assignees;
						task.perms = response.data.perms;
						task.priority = response.data.priority;
						task.is_complete = response.data.is_complete;
						task.duration = response.data.duration;
						task.duration_units = response.data.duration_units;
						task.is_complete_server = task.is_complete;

                        response.config.successCallback(response.data);
                    }, function (response) {
                        response.config.errorCallback(response.data);
					});
			},
			update: function(successCallback, errorCallback)
			{
				var data =
				{
					project_id: this.project_id,
					title: this.title.length <= 255 ? this.title : this.title.substr(0, 255),
                    description: this.description,
					due_date: this.due_date,
					is_private: this.is_private,
                    assignees: this.assignees,
                    priority: this.priority,
					duration: this.duration,
					is_complete: this.is_complete
				};

                // An array should contains only ids not user's objects
                if (Array.isArray(this.assignees) &&
                    this.assignees.length > 0  &&
                    this.assignees[0].hasOwnProperty('id')
                ) {
                    data.assignees = this.getUsersIds(this.assignees);
                }

				$http(
					{
						method:'PUT',
						url: this.restUrl + this.id,
						data: data,
						task: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
                    {
                        var task                = response.config.task;
                        task.id                 = response.data.id;
                        task.project_id         = response.data.project_id;
                        task.title              = response.data.title;
                        task.description        = response.data.description;
                        task.due_date           = response.data.due_date;
                        task.due_date_formatted = response.data.due_date_formatted;
                        task.due_date_type      = response.data.due_date_type;
                        task.is_private         = response.data.is_private;
                        task.created_by         = response.data.created_by;
                        task.assignees          = response.data.assignees;
                        task.perms              = response.data.perms;
                        task.priority           = response.data.priority;
                        task.is_complete        = response.data.is_complete;
                        task.duration           = response.data.duration;
                        task.duration_units     = response.data.duration_units;
                        task.is_complete_server = task.is_complete;

                        response.config.successCallback(task);
                    }, function (response) {
                        response.config.errorCallback(response.data);
                    });
				},
                delete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'DELETE',
                            url: this.restUrl + this.id,
                            section: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.successCallback(response.data.id);
                    }, function (response) {
                        response.config.errorCallback(response.data);
                    });
                },
                setComplete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'PUT',
                            url: this.restUrl + this.id + '/complete',
                            task: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.task.is_complete_server = true;
                        response.config.task.status_id = response.config.task.STATUS_COMPLETED;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                setIncomplete: function (successCallback, errorCallback) {
                    $http(
                        {
                            method: 'PUT',
                            url: this.restUrl + this.id + '/incomplete',
                            task: this,
                            successCallback: successCallback,
                            errorCallback: errorCallback
                        }).then(function (response) {
                        response.config.task.is_complete_server = false;
                        response.config.task.status_id = response.config.task.STATUS_TODO;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                setStatus: function (successCallback, errorCallback) {
                    if (!successCallback)
                        successCallback = function () {
                        };

                    if (!errorCallback)
                        errorCallback = function () {
                        };

                    var status      = this.status_id;
                    var is_complete = this.status_id === this.STATUS_COMPLETED;

                    $http({
                        method: "PUT",
                        url: '/api/projects/task/' + this.id + '/status',
                        task: this,
                        data: {
                            status: status
                        },
                        successCallback: successCallback,
                        errorCallback: errorCallback
                    }).then(function (response) {
                        response.config.task.is_complete_server = is_complete;
                        response.config.task.is_complete        = is_complete;
                        response.config.successCallback(response.config.task, response.data);
                    }, function (response) {
                        response.config.errorCallback(response.config.task, response.data);
                    });
                },
                moveToSectionId: function (section_id) {
                    this.section_id = section_id;
                },
                setDisplayOrder: function (display_order) {
                    this.display_order = display_order;
                },

                /**
                 * Change an array with objects contains assigned user to array with ids
                 *
                 * @param associative_array Array
                 * @returns [1,2,3,..] Array
                 */
                getUsersIds: function (associative_array) {
                    var assignees = [];

                    associative_array.map(function(assignee) {
                        if (!assignee.hasOwnProperty('id'))
                            return;

                        assignees.push(assignee.id);
                    })

                    return assignees;
                }
            };

        return Task;
    }
}());


/***/ }),

/***/ "./js/model/projectTaskSectionModel.js":
/***/ (function(module, exports) {

(function()
{
	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.factory('TaskSection', ['$http', 'Task', taskSectionFactory]);

	function taskSectionFactory($http, Task)
	{
		var TaskSection = function()
		{
			this.new_task = new Task();

			this.id = 0;
			this.project_id = 0;
			this.title = '';
			this.display_order = 0;
			this.colour = '#ccddee';
			this.style = {'background-color': '#ccddee'};
			this.expanded = true;

			this.editCopy =
			{
				title: this.title
			};

			this.new_task = {};
		};

		TaskSection.prototype =
		{
			restUrl: '/api/projects/task/section/',

			loadFromArray: function(data)
			{
				this.id = data.id;
				this.project_id = data.project_id;
				this.title = data.title;
				this.display_order = data.display_order;
				this.colour = data.colour;
				this.style = {'background-color': data.colour};
			},
			makeEditCopy: function()
			{
				this.editCopy =
				{
					title: this.title,
					colour: this.colour
				};
			},
			add: function(successCallback, errorCallback)
			{
				var data =
				{
					project_id: this.project_id,
					title: this.title,
					colour: this.colour,
					display_order: this.display_order
				};
				$http(
					{
						method:'POST',
						url: this.restUrl + 'add',
						data: data,
						section: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
                    {
                        var section = response.config.section;
                        section.id = response.data.id;
                        section.project_id = response.data.project_id;
                        section.title = response.data.title;
                        section.colour = response.data.colour;
                        section.style = {'background-color': response.data.colour};
                        section.display_order = response.data.display_order;

                        response.config.successCallback(response.data);
                    }, function(response)
                    {
                        response.config.errorCallback(response.data);
					});
			},
			update: function(successCallback, errorCallback)
			{
				var data =
				{
					title: this.editCopy.title,
					colour: this.editCopy.colour,
					project_id: this.project_id,
					display_order: this.display_order
				};
				$http(
					{
						method:'PUT',
						url: this.restUrl + this.id,
						data: data,
						section: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
                    {
                        var section = response.config.section;
                        section.id = response.data.id;
                        section.project_id = response.data.project_id;
                        section.title = response.data.title;
                        section.colour = response.data.colour;
                        section.style = {'background-color': response.data.colour};
                        section.display_order = response.data.display_order;

                        response.config.successCallback(response.data);
                    }, function(response)
                    {
                        response.config.errorCallback(response.data);
					});
			},
			delete: function(successCallback, errorCallback)
			{
				$http(
					{
						method:'DELETE',
						url: this.restUrl + this.id,
						section: this,
						successCallback: successCallback,
						errorCallback: errorCallback
					}).then(function(response)
                    {
                        response.config.successCallback(response.data.id);
                    }, function(response)
                    {
                        response.config.errorCallback(response.data);
					});
			},
			setDisplayOrder: function(display_order)
			{
				this.display_order = display_order;
			}
		};

		return TaskSection;
	}
}());


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/component/add_list_button.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/project/header_buttons/component/add_list_button.component.html';
var html = "<h6 class=\"mb-1 mt-0\"><lmsg src=\"projects.task.properties.list\">[List]</lmsg></h6>\n<p class=\"text-muted\">\n\t<lmsg src=\"projects.project.add.new_list\">[Add a new list]</lmsg>\n</p>\n<div class=\"btn-group\">\n\t<button class=\"btn btn-primary\"  data-toggle=\"modal\" data-target=\"#add-list-popup\" type=\"button\" ng-click=\"$ctrl.startEdit($event)\">\n\t\t<ion-icon name=\"add-outline\"></ion-icon>\n\t\t<lmsg src=\"projects.task.new_section.name\">[New List]</lmsg>\n\t</button>\n</div>\n\n<div class=\"modal fade\" id=\"add-list-popup\" tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"add-list-popup\">\n\t<div class=\"modal-dialog\" role=\"document\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" id=\"add-list--label\">\n\t\t\t\t\t<lmsg src=\"projects.task.new_section.name\">[New List]</lmsg>\n\t\t\t\t</h5>\n\t\t\t</div>\n\t\t\t<div class=\"modal-body\">\n\t\t\t\t<p><input type=\"text\" class=\"form-control\" size=\"40\" maxlength=\"255\" lmsg=\"placeholder:projects.task.new_section.name.placeholder\" ng-model=\"$ctrl.new_list.title\" autofocus=\"autofocus\"></p>\n\t\t\t\t<cla-colour-picker ng-model=\"$ctrl.new_list.colour\" id=\"new_section_colour\" control=\"$ctrl.colour_control\"></cla-colour-picker>\n\t\t\t\t<div ng-show=\"$ctrl.new_list.title.length > 255\">\n\t\t\t\t\t<br>\n\t\t\t\t\t<div class=\"alert alert-warning\">\n\t\t\t\t\t\t<lmsg src=\"projects.warning.field_longer_than_255\">[Length warning]</lmsg>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"modal-footer\">\n\t\t\t\t<div class=\"ml-auto\">\n\t\t\t\t\t<button class=\"btn btn-link\" ng-click=\"$ctrl.closeListModal()\" lmsg=\"projects.task.back\">[Back]</button>\n\t\t\t\t\t<button class=\"btn btn-light\" data-dismiss=\"modal\" lmsg=\"common.cancel\">[Cancel]</button>\n\t\t\t\t\t<button class=\"btn btn-primary\" ng-click=\"$ctrl.submitList($event)\" lmsg=\"common.save\">[Save]</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/project/header_buttons/component/add_list_button.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/project/header_buttons/component/add_list_button.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var DEFAULT_COLOUR = '#aa55cc';

    var addListButton = {
        bindings: {
            onNewList: '&'
        },
        templateUrl: template,
        controller: function () {
            var ctrl = this;
            var modalContainer = angular.element('#add-task-popup');

            ctrl.colour_control = {};

            ctrl.new_list = {
                title: lmsg('projects.task.new_section'),
                colour: DEFAULT_COLOUR
            };

            ctrl.submitList = function ($event) {
                $event.preventDefault();

                ctrl.onNewList({$event: {
                    title: ctrl.new_list.title,
                    colour: ctrl.new_list.colour,
                    onSuccess: function(){
                        angular.element('#add-list-popup').modal('hide');

                        // trigger when list has been added
                        angular.element('#headerButtonsApp').trigger('onAddedList', {});

                        // hide parent modal
                        if (modalContainer.length) {
                            modalContainer.modal('hide');
                        }
                    }
                }});
            };

            ctrl.closeListModal = function () {
                angular.element('#add-list-popup').modal('hide');
            };

            ctrl.startEdit = function($event)
            {
                // Reset form data
                ctrl.new_list.title = lmsg('projects.task.new_section');
                ctrl.new_list.colour = DEFAULT_COLOUR;
                ctrl.colour_control.reset();
            };
        }
    };

    return addListButton;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/component/add_task_button.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/project/header_buttons/component/add_task_button.component.html';
var html = "<h6 class=\"mb-1 mt-0\"><lmsg src=\"projects.task.new_task\">[New Task]</lmsg></h6>\n<p class=\"text-muted\">\n\t<lmsg src=\"projects.project.add.new_task\">[Add a new task]</lmsg>\n</p>\n<div class=\"btn-group\">\n\t<button class=\"btn btn-primary\" data-toggle=\"modal\" data-target=\"#add-task-popup\" type=\"button\" ng-click=\"$ctrl.startEdit($event)\">\n\t\t<ion-icon name=\"add-outline\"></ion-icon>\n\t\t<lmsg src=\"projects.task.new_task\">[New Task]</lmsg>\n\t</button>\n</div>\n\n<div class=\"modal fade js-task-add-modal\" id=\"add-task-popup\" tabindex=\"-2\" role=\"dialog\" aria-labelledby=\"add-task-popup\">\n\t<div class=\"modal-dialog modal-xl\" role=\"document\">\n\t\t<div class=\"modal-content\">\n\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" id=\"more-info--label\">\n\t\t\t\t\t<span lmsg=\"projects.task.modal.add_task\" ng-if=\"$ctrl.newTask.id==0\">[Add task]</span>\n\t\t\t\t\t<span lmsg=\"projects.task.modal.edit_task\" ng-if=\"$ctrl.newTask.id!=0\">[Edit task]</span>\n\t\t\t\t</h5>\n\t\t\t</div>\n\n\t\t\t<div class=\"modal-body\">\n\t\t\t\t<div class=\"form-horizontal\">\n\t\t\t\t\t<form id=\"task-edit-form\" class=\"form-horizontal\" name=\"taskForm\">\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-list\" class=\"col-form-label col-md-3 col-sm-3\" lmsg=\"projects.task.properties.list\">[List]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<div class=\"form-inline\">\n\t\t\t\t\t\t\t\t\t<select id=\"task-edit-list\" name=\"list\" ng-model=\"$ctrl.newTask.list_id\" class=\"form-control js-task-edit-list\" ng-options=\"list.id as list.title for list in $ctrl.lists\">\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"\" class=\"col-form-label col-md-3 col-sm-3\" lmsg=\"projects.task.edit.task_name\">[Task Name]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<input type=\"text\" maxlength=\"255\" class=\"form-control\" placeholder=\"Task Name\" ng-model=\"$ctrl.newTask.title\" lmsg=\"placeholder:projects.task.$ctrl.newTask_name\">\n\t\t\t\t\t\t\t\t<div ng-show=\"$ctrl.newTask.title.length > 255\">\n\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<div class=\"alert alert-warning\">\n\t\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.warning.field_longer_than_255\">[Length warning]</lmsg>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"newTaskDescription\" class=\"col-form-label col-md-3 col-sm-3\" lmsg=\"projects.task.edit.description\">[Description]</label>\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<cla-html-editor ng-model=\"$ctrl.newTask.description\" application=\"projects\" control=\"$ctrl.htmlControl\"></cla-html-editor>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-user-picker-inner\" class=\"col-form-label col-md-3 col-sm-3\" name=\"projects:task:assigned_to\" lmsg=\"projects.task.assigned_to\">[Assigned to]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9 xform-inline\">\n\t\t\t\t\t\t\t\t<clang-projects-user-picker select_id=\"{{$ctrl.listPrefix}}user-picker-add\" ng-model=\"$ctrl.newTask.assignees\" ng-change=\"taskAssigneeChanged($ctrl.task)\" project_id=\"{{$ctrl.projectId}}\" user_name=\"{{$ctrl.task.assigned_to.name}}\" allow_unset=\"0\" control=\"$ctrl.userControl\"></clang-projects-user-picker>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"{{edit.listPrefix}}due_date-{{edit.unique_id}}-inner\" class=\"col-form-label col-md-3 col-sm-3\" lmsg=\"projects.task.properties.due_date\">[Due date]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<cla-date-picker id=\"task-add-due_date\" ng-model=\"$ctrl.newTask.due_date\" control=\"$ctrl.newTask.control\" attach-to-input=\"true\"></cla-date-picker>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-priority\" class=\"col-form-label col-md-3 col-sm-3\" lmsg=\"projects.task.properties.priority\">[Priority]</label>\n\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<div class=\"form-inline\">\n\t\t\t\t\t\t\t\t\t<select id=\"task-edit-priority\" name=\"priority\" ng-model=\"$ctrl.newTask.priority\" class=\"form-control js-task-priorities js-task-edit-priority\">\n\t\t\t\t\t\t\t\t\t\t<option value=\"0\" lmsg=\"projects.task.priority.none\">[No priority]</option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"1\" lmsg=\"projects.task.priority.low\">[Low priority]</option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"2\" lmsg=\"projects.task.priority.medium\">[Medium priority]</option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"3\" lmsg=\"projects.task.priority.high\">[High priority]</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<div class=\"col-md-12 col-sm-12\"></div>\n\t\t\t\t\t\t\t<div class=\"offset-md-3 offset-sm-3 col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<div class=\"checkbox\">\n\t\t\t\t\t\t\t\t\t<label for=\"task-edit-private\">\n\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" id=\"task-edit-private\" ng-model=\"$ctrl.newTask.is_private\">\n\t\t\t\t\t\t\t\t\t\t<ion-icon name=\"lock-closed-outline\"></ion-icon>\n\t\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.task.properties.is_private\">[Private Task]</lmsg>\n\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p class=\"text-muted\">\n\t\t\t\t\t\t\t\t\t<span lmsg=\"projects.task.properties.is_private_message\">[Users can always see tasks assigned to them]</span>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t\t\t<label for=\"task-edit-duration\" class=\"col-md-3 col-sm-3 col-form-label\" lmsg=\"projects.task.expected_duration\">\n\t\t\t\t\t\t\t\t[expected duration]\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<div class=\"col-md-9 col-sm-9\">\n\t\t\t\t\t\t\t\t<div class=\"form-inline\">\n\t\t\t\t\t\t\t\t\t<input type=\"text\" name=\"duration\" id=\"task-edit-duration\" class=\"form-control mr-2\" size=\"2\" ng-model=\"$ctrl.newTask.duration\" ng-pattern=\"/^$|^(0|[1-9]\\d*)([.]\\d+)?$/\" value=\"0\">\n\t\t\t\t\t\t\t\t\t<span ng-bind=\"$ctrl.projectDurationUnits\"></span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p class=\"text-danger\" ng-if=\"taskForm.duration.$invalid\">\n\t\t\t\t\t\t\t\t\t<lmsg src=\"projects.task.duration_value_warning\">[Please input a valid duration value (number)]</lmsg>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"modal-footer\">\n\t\t\t\t<div class=\"ml-auto\">\n\t\t\t\t\t<button class=\"btn btn-link\" ng-click=\"$ctrl.closeTaskModal()\" lmsg=\"projects.task.back\">[Back]</button>\n\t\t\t\t\t<button class=\"btn btn-light\" data-dismiss=\"modal\" lmsg=\"projects.task.cancel\">[Cancel]</button>\n\t\t\t\t\t<button class=\"btn btn-primary\" ng-click=\"$ctrl.submitTask($event)\" ng-disabled=\"taskForm.duration.$invalid\" lmsg=\"projects.task.save_task\">[Save]</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/project/header_buttons/component/add_task_button.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/project/header_buttons/component/add_task_button.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var addTaskButton = {
        bindings: {
            projectId: '<',
            projectDurationUnits: '<',
            lists: '<',
            onNewTask: '&'
        },
        templateUrl: template,
        controller: [
            'projectPreloadFactory',
            '$timeout',
            'claTaskSectionService',
            function (projectPreload, $timeout, taskSectionService) {
                var ctrl = this;
                var modalContainer = angular.element('#add-task-or-list-popup');

                ctrl.userControl = {};
                ctrl.htmlControl = {};

                ctrl.$onInit = function() {
                    angular.element('#headerButtonsApp')
                        .on('onAddedList', function(event, data)
                        {
                            taskSectionService.get(projectPreload.id)
                                .then(function (response) {
                                    ctrl.lists = response;
                                    ctrl.listSelect();
                                });
                        })

                        .on('onDeleteList', function(event, data)
                        {
                            taskSectionService.get(projectPreload.id)
                                .then(function (response) {
                                    ctrl.lists = response;
                                    ctrl.listSelect();
                                });
                        });
                };

                ctrl.reset = function() {
                    ctrl.newTask = {
                        id: 0,
                        project_id: projectPreload.project_id,
                        title: '',
                        description: '',
                        due_date: '',
                        due_date_formatted: '',
                        list_id: ctrl.lists.length > 0 ? ctrl.lists[0].id : null,
                        is_private: false,
                        assignees: [],
                        priority: '0',
                        is_complete: false,
                        duration: '',
                        duration_units: '',
                        comments_count: 0,
                        has_file_storage: false,
                        display_order: 10000,
                        orders: {
                            task: ctrl.DISPLAY_ORDER_NONE,
                            board: ctrl.DISPLAY_ORDER_NONE,
                            board_no_list: ctrl.DISPLAY_ORDER_NONE,
                            my_tasks: ctrl.DISPLAY_ORDER_NONE
                        },
                        status_id: 0,
                        status_list: []
                    };
                };

                ctrl.closeTaskModal = function () {
                    angular.element('#add-task-popup').modal('hide');
                };

                ctrl.submitTask = function ($event) {
                    $event.preventDefault();

                    ctrl.onNewTask({
                        $data: {
                            name: ctrl.newTask.title,
                            description: ctrl.newTask.description,
                            assignees: ctrl.newTask.assignees,
                            due_date: ctrl.newTask.due_date,
                            priority: parseInt(ctrl.newTask.priority),
                            duration: parseInt(ctrl.newTask.duration),
                            is_private: ctrl.newTask.is_private,
                            list_id: parseInt(ctrl.newTask.list_id),
                            onSuccess: function() {
                                angular.element('#add-task-popup').modal('hide');

                                // update field "List" in Add task modal
                                angular.element('#headerButtonsApp').trigger('onAddedList', {});

                                // hide parent modal
                                if (modalContainer.length) {
                                    modalContainer.modal('hide');
                                }
                            }
                        }
                    });
                };

                ctrl.enableEditTaskPriority = function()
                {
                    angular.element('.js-task-edit-priority').each(function()
                    {
                        if (angular.element(this).hasClass('select2-container'))
                            return;

                        // This should be automatic but it is invisible still at this point so not processed yet.
                        angular.element(this).val(ctrl.newTask.priority);

                        ctrl.colourSelect(angular.element(this));
                    });
                };

                ctrl.colourSelect = function(target)
                {
                    target.select2({
                        templateResult: function (data)
                        {
                            if (!data.id) { return data.text; }
                            return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
                        },
                        templateSelection: function formatPrioritySelection (data)
                        {
                            if (!data.id) { return data.text; }
                            return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
                        },
                        escapeMarkup: function(markup)
                        {
                            return markup;
                        },
                        minimumResultsForSearch: Infinity, // Hide search box
                        width: "100%"
                    });
                };

                ctrl.listSelect = function()
                {
                    angular.element('.js-task-edit-list').select2({
                        width: "100%"
                    });
                    ctrl.userControl.reset();
                };

                ctrl.startEdit = function($event)
                {
                    ctrl.reset();

                    // Reset form data
                    ctrl.enableEditTaskPriority();

                    // Open CKEditor
                    ctrl.htmlControl.onOpen();

                    // init select2 after ctrl.reset()
                    $timeout(function() {
                        ctrl.listSelect();
                    });
                };

                angular.element('#add-task-popup').on('hidden.bs.modal', function(){
                    ctrl.htmlControl.onClose();
                });
            }
        ]
    };

    return addTaskButton;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_list.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/project/header_buttons/container/add_list.component.html';
var html = "<clang-add-list-button ng-if=\"$ctrl.canAddList()\" on-new-list=\"$ctrl.addNewList($event);\"></clang-add-list-button>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_list.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/project/header_buttons/container/add_list.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var addList = {
        bindings: {},
        templateUrl: template,
        controller: [
            '$timeout',
            '$scope',
            'projectPreloadFactory',
            function ($timeout, $scope, projectPreload) {
                var ctrl = this;

                ctrl.isVisible= false;
                angular.element(document).on('projectsTabChange', function($event, tabName) {
                    ctrl.isVisible = tabName === 'tasks';

                    $timeout(function() {
                        $scope.$digest();
                    }, 0);
                });

                ctrl.$onInit = function() {
                    ctrl.perms = projectPreload.perms;
                };

                ctrl.canAddList = function() {
                    return ctrl.isVisible && ctrl.perms.can_edit_any_task;
                };

                ctrl.addNewList = function (data) {
                    angular.element('#headerButtonsApp').trigger('onAddList', {
                        title: data.title,
                        colour: data.colour,
                        success: data.onSuccess,
                    });
                };
            }
        ]
    };

    return addList;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_task.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/project/header_buttons/container/add_task.component.html';
var html = "<clang-add-task-button ng-if=\"$ctrl.canAddTask()\" project-id=\"$ctrl.project_id\" project-duration-units=\"$ctrl.projectDurationUnits\" lists=\"$ctrl.lists\" on-new-task=\"$ctrl.addNewTask($data)\"></clang-add-task-button>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_task.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/project/header_buttons/container/add_task.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var AddTask = {
        bindings: {},
        templateUrl: template,
        controller: [
            '$timeout',
            '$scope',
            'projectPreloadFactory',
            function ($timeout, $scope, projectPreload) {
                var ctrl = this;

                ctrl.isVisible= false;
                angular.element(document).on('projectsTabChange', function($event, tabName) {
                    ctrl.isVisible = tabName === 'tasks';

                    $timeout(function() {
                        $scope.$digest();
                    }, 0);
                });

                ctrl.$onInit = function() {
                    ctrl.project_id = projectPreload.id;
                    ctrl.projectDurationUnits = projectPreload.duration_units;
                    ctrl.lists = projectPreload.lists;
                    ctrl.perms = projectPreload.perms;
                };

                ctrl.canAddTask = function() {
                    return ctrl.isVisible && ctrl.perms.can_add_task;
                };

                ctrl.addNewTask = function (data) {
                    angular.element('#headerButtonsApp').trigger('onAddTask', {
                        name: data.name,
                        description: data.description,
                        assignees: data.assignees,
                        due_date: data.due_date,
                        priority: data.priority,
                        duration: data.duration,
                        is_private: data.is_private,
                        list_id: data.list_id,
                        success: data.onSuccess
                    });
                };
            }
        ]
    };

    return AddTask;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_task_or_list.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/project/header_buttons/container/add_task_or_list.html';
var html = "<button ng-if=\"$ctrl.canAddTask() || $ctrl.canAddList()\" name=\"btn_create\" data-toggle=\"modal\" data-target=\"#add-task-or-list-popup\" rel=\"tooltip\" data-placement=\"bottom\" data-original-title=\"\" class=\"btn btn-link\">\n\t<ion-icon name=\"add-circle\" role=\"img\" class=\"md hydrated\" aria-label=\"add circle\"></ion-icon>\n</button>\n\n\n<div class=\"modal fade js-task-add-modal\" id=\"add-task-or-list-popup\" tabindex=\"-2\" role=\"dialog\" aria-labelledby=\"add-task-popup\">\n\t<div class=\"modal-dialog\" role=\"document\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" id=\"more-info--label\">\n\t\t\t\t\t<span lmsg=\"projects.project.add.new_task_list\">[Create new task or list]</span>\n\t\t\t\t</h5>\n\t\t\t\t<button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n\t\t\t\t\t<span aria-hidden=\"true\">\n\t\t\t\t\t\t<ion-icon name=\"close-outline\"></ion-icon>\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div class=\"modal-body overflow-hidden\">\n\t\t\t\t<div class=\"form-group row\">\n\t\t\t\t\t<div ng-show=\"$ctrl.canAddList()\" class=\"col-md-6 add-project-wrapper\">\n\t\t\t\t\t\t<!-- here only buttons !!!! -->\n\t\t\t\t\t\t<clang-add-list id=\"add_list\"></clang-add-list>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div ng-show=\"$ctrl.canAddTask()\" class=\"col-md-6 add-project-wrapper\">\n\t\t\t\t\t\t<clang-add-task id=\"add_task\"></clang-add-task>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/project/header_buttons/container/add_task_or_list.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/project/header_buttons/container/add_task_or_list.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var AddTaskOrList = {
        bindings: {},
        templateUrl: template,
        controller: [
            '$timeout',
            '$scope',
            'projectPreloadFactory',
            function ($timeout, $scope, projectPreload) {
                var ctrl = this;

                ctrl.isVisible= false;
                angular.element(document).on('projectsTabChange', function($event, tabName) {
                    ctrl.isVisible = tabName === 'tasks';

                    $timeout(function() {
                        $scope.$digest();
                    }, 0);
                });

                ctrl.$onInit = function() {
                    ctrl.perms = projectPreload.perms;
                };

                ctrl.canAddTask = function() {
                    return ctrl.isVisible && ctrl.perms.can_add_task;
                };

                ctrl.canAddList = function() {
                    return ctrl.isVisible && ctrl.perms.can_edit_any_task;
                };
            }
        ]
    };

    return AddTaskOrList;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/project/header_buttons/header_buttons.module.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
    __webpack_require__("./js/ng/modules/project/header_buttons/container/add_list.component.js"),
    __webpack_require__("./js/ng/modules/project/header_buttons/component/add_list_button.component.js"),
    __webpack_require__("./js/ng/modules/project/header_buttons/container/add_task.component.js"),
    __webpack_require__("./js/ng/modules/project/header_buttons/component/add_task_button.component.js"),
    __webpack_require__("./js/ng/modules/project/header_buttons/container/add_task_or_list.js"),
    __webpack_require__(11),
    __webpack_require__(0),
    __webpack_require__(12)
], __WEBPACK_AMD_DEFINE_RESULT__ = function (
    addList,
    addListButton,
    addTask,
    addTaskButton,
    addTaskOrList
) {
    return angular
        .module('claromentis.projects.header_buttons', ['cla.core.date_picker', 'cla.core.colour_picker', 'cla.core.html_editor', 'cla.core.lmsg', 'claromentis.projects.widgets'])

        // Containers
        .component('clangAddList', addList)
        .component('clangAddTask', addTask)

        // Presentational
        .component('clangAddTaskButton', addTaskButton)
        .component('clangAddListButton', addListButton)

        .component('clangAddTaskOrList', addTaskOrList);
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/checkbox.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/checkbox.component.html';
var html = "<span>\n\t<cla-checkbox large=\"{{$ctrl.large}}\" name=\"{{$ctrl.name}}\" ng-attr-checked=\"{{$ctrl.ngModel?true:undefined}}\" ng-attr-disabled=\"{{$ctrl.disabled?true:undefined}}\" ng-on-selected=\"$ctrl.onChecked($event)\" ng-on-unselected=\"$ctrl.onUnchecked($event)\" ng-transclude></cla-checkbox>\n</span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/checkbox.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/checkbox.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var Checkbox = {
        bindings: {
            ngModel: '=',
            large: '@',
            name: '@',
            disabled: '@',
        },
        require: { ngModelCtrl: 'ngModel' },
        templateUrl: template,
        transclude: true,
        controller: function () {
            var ctrl = this;

            ctrl.$onInit = function() {
                if (ctrl.disabled === undefined)
                    ctrl.disabled = false;
            };

            ctrl.onChecked = function($event) {
                $event.preventDefault();

                ctrl.ngModel = true;
                ctrl.ngModelCtrl.$setViewValue(ctrl.ngModel);
            }

            ctrl.onUnchecked = function($event) {
                $event.preventDefault();

                ctrl.ngModel = false;
                ctrl.ngModelCtrl.$setViewValue(ctrl.ngModel);
            }
        }
    };

    return Checkbox;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/confirm.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/confirm.component.html';
var html = "<div class=\"modal fade\" id=\"projects-confirm\" tabindex=\"-1\" role=\"dialog\" aria-labelledby=\"more-info\">\n\t<div class=\"modal-dialog\" role=\"document\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" id=\"more-info--label\">{{$ctrl.options.title}}</h5>\n\t\t\t\t<button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n\t\t\t\t\t<span aria-hidden=\"true\">\n\t\t\t\t\t\t<ion-icon name=\"close-outline\"></ion-icon>\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div class=\"modal-body\">\n\n\t\t\t\t<div class=\"media\">\n\t\t\t\t\t<div class=\"mr-3 align-self-center\">\n\t\t\t\t\t\t<span class=\"text-muted\">\n\t\t\t\t\t\t\t<ion-icon class=\"icon-xxl align-middle\" name=\"{{$ctrl.options.icon}}\"></ion-icon>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"media-body align-self-center\">\n\t\t\t\t\t\t<p class=\"mb-0\">{{$ctrl.options.message}}</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t</div>\n\t\t\t<div class=\"modal-footer\">\n\t\t\t\t<form id=\"task-edit-form\" class=\"form-horizontal\" name=\"taskForm\">\n\t\t\t\t\t<div ng-if=\"$ctrl.options.mode === 'cancel_delete'\">\n\t\t\t\t\t\t<button class=\"btn btn-link\" ng-click=\"$ctrl.cancel($event)\" lmsg=\"projects.task.cancel\">[Cancel]</button>\n\t\t\t\t\t\t<button class=\"btn btn-danger\" rel=\"tooltip\" ng-click=\"$ctrl.confirm($event)\"><lmsg src=\"projects.task.delete\">[Delete]</lmsg></button>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div ng-if=\"$ctrl.options.mode === 'ok'\">\n\t\t\t\t\t\t<button class=\"btn btn-primary\" ng-click=\"$ctrl.confirm($event)\" lmsg=\"common.ok\">[Ok]</button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/confirm.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/confirm.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var Confirm = {
        bindings: {},
        templateUrl: template,
        transclude: true,
        controller: [
            'confirmService',
            function (confirmService) {
                var ctrl = this;

                ctrl.options = {
                    title: '',
                    message: '',
                    icon: '',
                    mode: ''
                };

                ctrl.settings = function(settings) {
                    var values = {
                        title: '',
                        message: '',
                        icon: 'alert-circle-outline',
                        mode: 'cancel_delete'
                    };
                    ctrl.options = angular.extend(values, settings);
                };
                confirmService.registerConfirmDialog(ctrl.settings);

                ctrl.confirm = function() {
                    confirmService.resolve();
                };

                ctrl.cancel = function() {
                    confirmService.reject();
                };
            }
        ]
    };

    return Confirm;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/toggle_switch.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/toggle_switch.component.html';
var html = "<!-- Hack to assign different values to the \"ontext\" property which is interpreted as an event handler due to the name -->\n<span>\n\t<cla-toggle-switch ng-if=\"$ctrl.labels !== 'showhide'\" lmsg=\"ontext:projects.details.grouped\" offtext=\"{{$ctrl.offLabel()}}\" large=\"{{$ctrl.large}}\" ng-attr-checked=\"{{$ctrl.ngModel?true:undefined}}\" name=\"{{$ctrl.name}}\" ng-attr-disabled=\"{{$ctrl.disabled?true:undefined}}\" ng-on-selected=\"$ctrl.onToggledOn($event)\" ng-on-unselected=\"$ctrl.onToggledOff($event)\" ng-transclude></cla-toggle-switch>\n\t<cla-toggle-switch ng-if=\"$ctrl.labels === 'showhide'\" lmsg=\"ontext:projects.details.show\" offtext=\"{{$ctrl.offLabel()}}\" large=\"{{$ctrl.large}}\" ng-attr-checked=\"{{$ctrl.ngModel?true:undefined}}\" name=\"{{$ctrl.name}}\" ng-attr-disabled=\"{{$ctrl.disabled?true:undefined}}\" ng-on-selected=\"$ctrl.onToggledOn($event)\" ng-on-unselected=\"$ctrl.onToggledOff($event)\" ng-transclude></cla-toggle-switch>\n</span>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/toggle_switch.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/toggle_switch.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var ToggleSwitch = {
        bindings: {
            ngModel: '=',
            labels: '@',
            labelOn: '@',   // Can't be used until the web component property can be renamed away from 'ontext'
            labelOff: '@',  // Also not used due to above limitation
            large: '@',
            name: '@',
            disabled: '@'
        },
        require: { ngModelCtrl: 'ngModel' },
        templateUrl: template,
        transclude: true,
        controller: function () {
            var ctrl = this;

            ctrl.$onInit = function() {
                if (ctrl.disabled === undefined)
                    ctrl.disabled = false;
            };

            ctrl.offLabel = function() {
                if (ctrl.labels !== 'showhide')
                    return lmsg('projects.details.not_grouped');
                else
                    return lmsg('projects.details.hide');
            };

            ctrl.onToggledOn = function($event) {
                $event.preventDefault();

                ctrl.ngModel = true;
                ctrl.ngModelCtrl.$setViewValue(ctrl.ngModel);
            }

            ctrl.onToggledOff = function($event) {
                $event.preventDefault();

                ctrl.ngModel = false;
                ctrl.ngModelCtrl.$setViewValue(ctrl.ngModel);
            }
        }
    };

    return ToggleSwitch;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/user_list.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/user_list.component.html';
var html = "<div class=\"user-list-wrapper user-list-component\">\n\t<div class=\"user-list\">\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class=\"plus-number-wrapper\" ng-if=\"$ctrl.plusnumber > 0\" ng-click=\"$ctrl.plusClicked($event)\">\n\t\t\t<a ng-href=\"javascript:void(0)\" class=\"plus-number\">+{{$ctrl.plusnumber}}</a>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/user_list.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/user_list.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var UserList = {
        bindings: {
            plusnumber: '@',
            onPlusClicked: '&'
        },
        transclude: true,
        templateUrl: template,
        controller: [
            function () {
                var ctrl = this;

                ctrl.plusClicked = function($event) {
                    $event.preventDefault();

                    ctrl.onPlusClicked();
                }
            }
        ]
    };

    return UserList;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/user_list_item.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/user_list_item.component.html';
var html = "<div class=\"user-list-item square\" data-original-title=\"{{$ctrl.name}}\" rel=\"tooltip\"><a ng-href=\"{{$ctrl.profile}}\" target=\"_parent\"><img ng-src=\"{{$ctrl.img}}\"/></a></div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/user_list_item.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/user_list_item.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var UserListItem = {
        bindings: {
            img: '@',
            name: '@',
            profile: '@'
        },
        templateUrl: template,
        controller: [
            function () {
                var ctrl = this;
            }
        ]
    };

    return UserListItem;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/component/users_list_popup.component.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/component/users_list_popup.component.html';
var html = "<div class=\"modal fade js-users_list_modal\" role=\"dialog\">\n\t<div class=\"modal-dialog\" role=\"document\">\n\t\t<div class=\"modal-content\">\n\t\t\t<div class=\"modal-header\">\n\t\t\t\t<h5 class=\"modal-title\" lmsg=\"projects.task.user_list\">[User list]</h5>\n\t\t\t\t<button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Cancel\">\n\t\t\t\t\t<span aria-hidden=\"true\">\n\t\t\t\t\t\t<ion-icon name=\"close-outline\"></ion-icon>\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div class=\"modal-body\">\n\t\t\t\t<div class=\"d-inline-flex\"\n\t\t\t\t\t dir-paginate=\"user in $ctrl.paginatedUsers | itemsPerPage: $ctrl.usersPerPage\"\n\t\t\t\t\t pagination-id=\"user_list_popup\"\n\t\t\t\t\t total-items=\"$ctrl.totalItems\"\n\t\t\t\t\t current-page=\"$ctrl.currentPage\">\n\t\t\t\t\t<span ng-if=\"user.profile_url === null\" class=\"btn btn-link disabled\">\n\t\t\t\t\t\t<img class=\"tasks-profile-img profile-img mr-2\" ng-src=\"{{user.profile_img}}\">{{user.name}}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<a ng-if=\"user.profile_url !== null && user.name != ''\" href=\"{{user.profile_url}}\" class=\"btn btn-link\" rel=\"tooltip\" title=\"{{user.name}}\">\n\t\t\t\t\t\t<img class=\"tasks-profile-img profile-img mr-2\" ng-src=\"{{user.profile_img}}\">{{user.name}}\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"text-center clearfix\">\n\t\t\t\t\t<dir-pagination-controls\n\t\t\t\t\t\t\tpagination-id=\"user_list_popup\"\n\t\t\t\t\t\t\ton-page-change=\"$ctrl.pageChanged(newPageNumber)\"\n\t\t\t\t\t\t\ttemplate-url=\"/intranet/projects/html/components/project_list/pagination.html\"\n\t\t\t\t\t></dir-pagination-controls>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/component/users_list_popup.component.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/component/users_list_popup.component.html")], __WEBPACK_AMD_DEFINE_RESULT__ = function (template)
{
    var UsersListPopup = {
        bindings: {},
        templateUrl: template,
        controller: [
            '$element',
            'userListService',
            function ($element, userListService) {
                var ctrl = this;

                ctrl.users = [];
                ctrl.currentPage = 1;
                ctrl.usersPerPage = 10;
                ctrl.totalItems = 0;
                ctrl.paginatedUsers = []

                ctrl.$onInit = function() {
                    $element.find('.js-users_list_modal').on('update', function(event, users) {
                        ctrl.users = users;

                        ctrl.currentPage = 1;
                        ctrl.totalItems = ctrl.users.length;
                        ctrl.pageChanged(ctrl.currentPage);
                    });
                };

                // If pagination is clicked
                ctrl.pageChanged = function(newPage)
                {
                    ctrl.paginatedUsers = paginate(ctrl.users, ctrl.usersPerPage, newPage);
                };

                function paginate(users, usersPerPage, currentPage) {
                    return users.slice((currentPage - 1) * usersPerPage, currentPage * usersPerPage);
                }
            }
        ]
    };

    return UsersListPopup;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/css/user_list.component.scss":
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__("../../../node_modules/css-loader/index.js!../../../node_modules/sass-loader/lib/loader.js!./js/ng/modules/widgets/css/user_list.component.scss");
if(typeof content === 'string') content = [[module.i, content, '']];
// add the styles to the DOM
var update = __webpack_require__("../../../node_modules/style-loader/addStyles.js")(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../../../../../../../node_modules/css-loader/index.js!../../../../../../../../node_modules/sass-loader/lib/loader.js!./user_list.component.scss", function() {
			var newContent = require("!!../../../../../../../../node_modules/css-loader/index.js!../../../../../../../../node_modules/sass-loader/lib/loader.js!./user_list.component.scss");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ "./js/ng/modules/widgets/directive/user_picker.directive.html":
/***/ (function(module, exports) {

var path = '/js/ng/modules/widgets/directive/user_picker.directive.html';
var html = "<div class=\"row\">\n\t<div class=\"col-md-8 mb-2 mb-sm-0\">\n\t\t<select name=\"user_picker_select\" id=\"{{id}}\" class=\"form-control\" size=\"30\" multiple=\"multiple\"></select>\n\t</div>\n\t<div class=\"col-md-4\">\n\t\t<a href=\"#\" id=\"browse_{{id}}\" class=\"btn btn-secondary\" name=\"select_link\" ng-click=\"browse($event)\"><txt name=\"common:user_picker:browse\">Browse</txt></a>\n\t</div>\n</div>\n";
window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]);
module.exports = path;

/***/ }),

/***/ "./js/ng/modules/widgets/directive/user_picker.directive.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__("./js/ng/modules/widgets/directive/user_picker.directive.html"), __webpack_require__(3)], __WEBPACK_AMD_DEFINE_RESULT__ = function (template, $)
{
    UserPicker.$inject = ['$timeout'];
    function UserPicker($timeout)
    {
        return {
            restrict: 'E',
            require: '^ngModel',
            templateUrl: template,
            scope: {
                ngModel: '=',
                control: '=',
                allowBlocked: '@'
            },
            link: link
        };

        function link(scope, iElement, iAttrs, ctrl)
        {
            scope.id = iAttrs.selectId + '-inner';
            ctrl.user_name = iAttrs.userName;

            var allow_unset = iAttrs.allowUnset == 1;

            // Get project ID if there is one to limit search results
            scope.project_id = iAttrs.projectId || 0;
            scope.project_rights = iAttrs.projectRights || 0;
            scope.project_param = '';

            if ((typeof(scope.project_id) !== 'undefined') &&
                (scope.project_id > 0))
            {
                scope.project_param = '?project_id=' + scope.project_id;
            }

            // Don't use the system user_picker_popup.php because we need to filter results to a projects team members if needed
            ctrl.UserPicker_InitSelect2 = function (element_id, project_id, project_rights, placeholder, allow_unset) {
                var el = jq_$(element_id);
                var page_size = 50;

                var e = el.select2({
                    placeholder: placeholder,
                    minimumInputLength: 3,
                    width: '100%',
                    allowClear: allow_unset,
                    ajax: {
                        url: "/api/projects/v2/projects/" + project_id + "/users",
                        dataType: 'json',
                        delay: 250,
                        data: function (params) {
                            var page = params.page || 1;
                            return {
                                name: params.term, // search term
                                limit: page_size,
                                offset: (page-1) * page_size,
                                fields: "fullname"
                            };
                        },
                        processResults: function (data, params) {
                            params.page = params.page || 1;
                            var list = data.data.map(function(elem){
                                return {
                                    id: elem.id,
                                    text: elem.fullname
                                }
                            });

                            if (!!scope.allowBlocked) {
                                list.push({id: 0, text: lmsg('projects.task.user_blocked')});
                            }

                            return {
                                results: list,
                                pagination: {
                                    more: data.pagination.next != null
                                }
                            };
                        }
                    }
                });
            };

            // Angular won't have populated the control/browse ids yet so wait until the current event is finished
            $timeout(function(id, text, allow_unset, scope){
                scope.initSelect2(id, text, allow_unset)
            }, 0, true, scope.id, lmsg('projects.picker.select_user'), allow_unset, scope);

            if (scope.ngModel !== undefined && scope.ngModel !== null && Array.isArray(scope.ngModel)) {
                scope.ngModel.map(function (user) {
                    $('#' + scope.id).append($('<option>').attr('value', user.id).attr('selected', 'selected').text(user.name));
                });
            }

            ctrl.UserPicker_InitSelect2(scope.id, scope.project_id, scope.project_rights, lmsg('projects.picker.select_user'), allow_unset);

            ctrl.$viewChangeListeners.push(function() {
                scope.$eval(iAttrs.ngChange);
            });

            scope.initSelect2 = function(id, text, allow_unset)
            {
                var select = $('#' + id);
                select.find('option').remove();

                // Add an entry for the current user if needed
                if (scope.ngModel !== undefined && scope.ngModel !== null && Array.isArray(scope.ngModel)) {
                    scope.ngModel.map(function (user) {
                        select.append($('<option>').attr('value', user.id).text(user.name).attr('selected', 'selected'));
                    });
                }

                select.on('change', function() {
                    var $el = angular.element(this);
                    var user_ids = $el.val();

                    // cast variables to int
                    user_ids = user_ids.map(function (user_id) {
                        return parseInt(user_id);
                    });

                    var user_id = user_ids.slice(-1)[0];
                    var name = user_id === '' ? '' : $el.find('option[value=' + user_id + ']').text();

                    scope.change(user_ids, name);
                });

                ctrl.UserPicker_InitSelect2(scope.id, scope.project_id, scope.project_rights, text, allow_unset);
            };

            /**
             * @param user_ids Array
             * @param name string
             */
            scope.change = function(user_ids, name)
            {
                if (!Array.isArray(user_ids))
                    user_ids = [];

                scope.user_name = name;
                ctrl.$setViewValue(user_ids);
                ctrl.$render();

                $timeout(function(user_ids) {
                    ctrl.$setViewValue(user_ids);
                    ctrl.$render();
                }, 0, true, user_ids);
            };

            scope.browse = function(event)
            {
                event.preventDefault();
                var project_params = '';
                if (scope.project_id > 0)
                    project_params = '&project_id=' + scope.project_id;
                var recwnd = window.open('/projects/user_picker_popup?input_dom_id=' + scope.id + project_params + '&multi=1','user_picker_wnd','width=800,height=450,scrollbars=yes,resizable=yes,hotkeys=no,maximize=no');
                recwnd.focus();
            };

            scope.internalControl = scope.control || {};
            scope.internalControl.reset = function(user_name)
            {
                // Rebind the model. This shouldn't be needed but it never gets updated once reset otherwise
                ctrl.$setViewValue(scope.ngModel);

                scope.initSelect2(scope.id, lmsg('projects.picker.select_user'), true);

                var select = $('#' + scope.id);
                select.find('option').remove();
            };
        }
    }

    return UserPicker;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/projects_widgets.module.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
    __webpack_require__("./js/ng/modules/widgets/directive/user_picker.directive.js"),
    __webpack_require__("./js/ng/modules/widgets/component/toggle_switch.component.js"),
    __webpack_require__("./js/ng/modules/widgets/component/confirm.component.js"),
    __webpack_require__("./js/ng/modules/widgets/component/user_list.component.js"),
    __webpack_require__("./js/ng/modules/widgets/component/user_list_item.component.js"),
    __webpack_require__("./js/ng/modules/widgets/component/users_list_popup.component.js"),
    __webpack_require__("./js/ng/modules/widgets/component/checkbox.component.js"),
    __webpack_require__("./js/ng/modules/widgets/service/confirm.service.js"),
    __webpack_require__("./js/ng/modules/widgets/service/user_list.service.js"),
    __webpack_require__("./node_modules/angular-utils-pagination/dirPagination.js"),
    __webpack_require__("./js/ng/modules/widgets/css/user_list.component.scss")

], __WEBPACK_AMD_DEFINE_RESULT__ = function (
    UserPicker,
    ToggleSwitch,
    Confirm,
    UserList,
    UserListItem,
    UsersListPopup,
    Checkbox,
    ConfirmService,
    UserListService
) {
    return angular
        .module('claromentis.projects.widgets', ['angularUtils.directives.dirPagination'])
        .directive('clangProjectsUserPicker', UserPicker)
        .component('clangToggleSwitch', ToggleSwitch)
        .component('clangConfirm', Confirm)
        .component('clangUserList', UserList)
        .component('clangUserListItem', UserListItem)
        .component('clangUsersListPopup', UsersListPopup)
        .component('clangCheckbox', Checkbox)
        .service('confirmService', ConfirmService)
        .service('userListService', UserListService);
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/service/confirm.service.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function ()
{
    var ConfirmService = function($q) {
        var service = this;

        service.promise = null;
        service.confirmDialog = null;

        service.registerConfirmDialog = function(dialog) {
            if (service.confirmDialog !== null)
                console.error('ConfirmService error: Multiple <clang-confirm> instances exist, only one should be defined per page');

            service.confirmDialog = dialog;
        };

        service.show = function(settings) {
            if (service.confirmDialog === null)
            {
                console.error('Confirmation dialog not found');
                return;
            }
            service.confirmDialog(settings);

            angular.element('#projects-confirm').modal('show');
            service.promise = $q.defer();

            return service.promise.promise;
        };

        service.resolve = function() {
            angular.element('#projects-confirm').modal('hide');
            service.promise.resolve();
        };

        service.reject = function() {
            angular.element('#projects-confirm').modal('hide');
            service.promise.reject();
        };
    };
    ConfirmService.$inject = ['$q'];

    return ConfirmService;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/ng/modules/widgets/service/user_list.service.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function ()
{
    var template = '<clang-users-list-popup></clang-users-list-popup>';

    var UserListService = function($rootScope, $rootElement, $compile, $timeout) {
        var service = this;

        service.users = [];

        service.showUsersList = function(users) {
            service.users = users;

            var modal = $rootElement.find('.js-users_list_modal');
            if (modal.length === 0)
            {
                // Add the template to the end of this applications root element
                var newScope = $rootScope.$new(true);
                $rootElement.append($compile(template)(newScope));
            }

            $timeout(function() {
                $rootScope.$digest();

                modal = $rootElement.find('.js-users_list_modal');
                modal.trigger('update', [service.users]);
                modal.modal('show');
            }, 0);
        };
    };
    UserListService.$inject = ['$rootScope', '$rootElement', '$compile', '$timeout'];

    return UserListService;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/project/tasks.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/project/tasks.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangProjectTasks', projectTask);

    function projectTask() {
        controller.$inject = ['$rootScope', '$localStorage', 'claProjectDetailsService'];

        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                tasks: '<',
                lists: '<',
                statuses: '<',
                perms: '<',
                archiveDate: '<'
            },
            controllerAs: 'tasks',
            bindToController: true,
            controller: controller,
            link: link
        };

        function controller($rootScope, $localStorage, detailsService)
        {
            var ctrl = this;

            // These two are kept in local storage to remember user preferences
            ctrl.viewMode = 'list';
            ctrl.groupByList = true

            ctrl.showCompleted = false;
            ctrl.isMobile = true;
            ctrl.formattedTasks = {
                lists: []
            };

            ctrl.hideText = lmsg('projects.details.hide');
            ctrl.notGroupedText = lmsg('projects.details.not_grouped');

            this.$onInit = function() {
                ctrl.localStorageViewPrefsKey = 'projectTasks-ViewPrefs-' + ctrl.perms.user_id;

                if ($localStorage[ctrl.localStorageViewPrefsKey] !== undefined)
                {
                    if ($localStorage[ctrl.localStorageViewPrefsKey].viewMode !== undefined)
                        ctrl.viewMode = $localStorage[ctrl.localStorageViewPrefsKey].viewMode;
                    if ($localStorage[ctrl.localStorageViewPrefsKey].groupByList !== undefined)
                        ctrl.groupByList = $localStorage[ctrl.localStorageViewPrefsKey].groupByList;
                } else
                {
                    $localStorage[ctrl.localStorageViewPrefsKey] = {
                        viewMode: ctrl.viewMode,
                        groupByList: ctrl.groupByList
                    };
                }

                ctrl.buildTasks(ctrl.viewMode);
            };

            $rootScope.$on('task_list_data.updated', function($event, data) {
                var viewMode = ctrl.viewMode;

                if ((data !== undefined) && (data.view !== undefined))
                    viewMode = data.view;

                $localStorage[ctrl.localStorageViewPrefsKey].viewMode = viewMode;

                ctrl.buildTasks(viewMode);

                $rootScope.$broadcast('updateHistoryLog', null);
            });

            ctrl.onToggleVisibility = function() {
                $localStorage[ctrl.localStorageViewPrefsKey].groupByList = ctrl.groupByList;

                ctrl.buildTasks(ctrl.viewMode);
            }

            ctrl.buildTasks = function(viewMode) {

                var taskCount = 0;
                ctrl.formattedTasks.lists.length = 0;

                if (viewMode === 'board')
                {
                    if (ctrl.groupByList)
                    {
                        for (var listIndex = 0; listIndex < ctrl.lists.length; listIndex++)
                        {
                            var statuses = [];

                            for (var statusIndex = 0; statusIndex < ctrl.statuses.length; statusIndex++)
                            {
                                var tasks = [];

                                for (var taskIndex = 0; taskIndex < ctrl.tasks.length; taskIndex++)
                                {
                                    if ((ctrl.tasks[taskIndex].section_id == ctrl.lists[listIndex].id) &&
                                        (ctrl.tasks[taskIndex].status_id == ctrl.statuses[statusIndex].id) &&
                                        ((ctrl.tasks[taskIndex].date_completed > ctrl.archiveDate) || (ctrl.tasks[taskIndex].date_completed === null)))
                                    {
                                        tasks.push(ctrl.tasks[taskIndex]);
                                    }
                                }

                                // Sort by board_order
                                tasks.sort(function(a, b){
                                    if (a.orders.board < b.orders.board)
                                        return -1;
                                    if (a.orders.board > b.orders.board)
                                        return 1;
                                    return 0;
                                });

                                statuses.push({
                                    status: ctrl.statuses[statusIndex],
                                    tasks: tasks
                                });

                                taskCount += tasks.length;
                            }

                            ctrl.formattedTasks.lists.push({
                                list: ctrl.lists[listIndex],
                                listId: parseInt(ctrl.lists[listIndex].id),
                                listName: ctrl.lists[listIndex].title,
                                statuses: statuses
                            });
                        }
                    } else
                    {
                        var statuses = [];

                        for (var statusIndex = 0; statusIndex < ctrl.statuses.length; statusIndex++)
                        {
                            var tasks = [];

                            for (var taskIndex = 0; taskIndex < ctrl.tasks.length; taskIndex++)
                            {
                                if ((ctrl.tasks[taskIndex].status_id === ctrl.statuses[statusIndex].id) &&
                                    ((ctrl.tasks[taskIndex].date_completed > ctrl.archiveDate) || (ctrl.tasks[taskIndex].date_completed === null)))
                                {
                                    var task = ctrl.tasks[taskIndex];

                                    var listColour = '#FFFFFF';
                                    for (var listIndex = 0; listIndex < ctrl.lists.length; listIndex++)
                                    {
                                        if (ctrl.lists[listIndex].id == task.section_id)
                                            listColour = ctrl.lists[listIndex].colour;
                                    }
                                    task.listColour = listColour;

                                    tasks.push(task);
                                }
                            }

                            // Sort by board_order
                            tasks.sort(function(a, b){
                                if (a.orders.board_no_list < b.orders.board_no_list)
                                    return -1;
                                if (a.orders.board_no_list > b.orders.board_no_list)
                                    return 1;
                                return 0;
                            });

                            statuses.push({
                                status: ctrl.statuses[statusIndex],
                                tasks: tasks
                            });

                            taskCount += tasks.length;
                        }

                        ctrl.formattedTasks.lists.push({
                            listColour: listColour,
                            statuses: statuses
                        });
                    }
                } else
                {
                    for (var listIndex = 0; listIndex < ctrl.lists.length; listIndex++)
                    {
                        var tasks = [];

                        for (var taskIndex = 0; taskIndex < ctrl.tasks.length; taskIndex++)
                        {
                            if ((ctrl.tasks[taskIndex].section_id == ctrl.lists[listIndex].id) &&
                                ((ctrl.showCompleted) ||
                                (ctrl.tasks[taskIndex].status_id != -2)))
                            {
                                tasks.push(ctrl.tasks[taskIndex]);
                            }
                        }

                        // Sort by display_order
                        tasks.sort(function(a, b){
                            if (a.orders.list < b.orders.list)
                                return -1;
                            if (a.orders.list > b.orders.list)
                                return 1;
                            return 0;
                        });

                        ctrl.formattedTasks.lists.push({
                            listName: ctrl.lists[listIndex].title,
                            listId: parseInt(ctrl.lists[listIndex].id),
                            list: ctrl.lists[listIndex],
                            tasks: tasks
                        });

                        taskCount += tasks.length;
                    }
                }

                // Hide any visible tooltips before the screen is redrawn
                angular.element('a[rel="tooltip"]').tooltip('hide');

                // Update the sidebar with current visible task count
                detailsService.setTaskCount(taskCount, ctrl.tasks.length);
            }
        }

        function link(scope, element, attr, ctrl) {

            var mobileWidth = 767;

            ctrl.isMobile = document.body.clientWidth <= mobileWidth;

            angular.element(window).on('resize', function(event) {
                ctrl.isMobile = document.body.clientWidth <= mobileWidth;
                scope.$digest();
            });
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks.list_type.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/project/tasks.list_type.directive.html");
    var moduleName = 'claromentis.project';

    angular.module(moduleName)
        .directive('clangTasksListType', tasksListType);

    tasksListType.$inject = ['$rootScope'];
    function tasksListType($rootScope) {
        return {
            restrict: 'E',
            require: '^ngModel',
            templateUrl: template,
            scope: {
                ngModel: '='
            },
            transclude: true,
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            this.$onInit = function() {
                if ((ctrl.ngModel !== 'list') &&
                    (ctrl.ngModel !== 'board'))
                {
                    ctrl.ngModel = 'list';
                }
            };

            ctrl.changeView = function(view, $event)
            {
                $event.preventDefault();

                ctrl.ngModel = view === 'list' ? 'list' : 'board';

                // Pass view mode now so that the new data can be built correctly before the end of the digest
                $rootScope.$broadcast('task_list_data.updated', {view: ctrl.ngModel});
            };
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks.permission_checker.class.js":
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() {
    function TasksPermissionChecker(perms) {
        this.perms = {
            can_update_own_task_status: false,
            can_edit_own_task: false,
            can_edit_any_task: false,
            can_edit_project: false
        };
        for (var attrName in perms) { this.perms[attrName] = perms[attrName]; }
    }

    TasksPermissionChecker.prototype.perms = {};

    TasksPermissionChecker.prototype.HasTaskDragPerms = function(task, listId) {
        if (this.perms.can_edit_any_task)
            return true;

        // Task container and no other permission
        if (task === undefined && listId === undefined && !this.perms.can_edit_own_task && !this.perms.can_update_own_task_status)
            return false;

        // Task container and have either permission
        if ((task === undefined && listId === undefined) && (this.perms.can_edit_own_task || this.perms.can_update_own_task_status))
            return true;

        // Task check specifically
        return this.CanEditMyOwnTask(task) || this.CanUpdateMyTaskStatus(task, listId);
    };

    TasksPermissionChecker.prototype.HasListDragPerms = function() {
        return this.perms.can_edit_any_task;
    };

    TasksPermissionChecker.prototype.HasStatusDragPerms = function() {
        return this.perms.can_edit_project;
    };

    TasksPermissionChecker.prototype.IsMyTask = function(task) {
        if (parseInt(task.created_by.id) === this.perms.user_id)
            return true;

        for (var assigneeIndex = 0; assigneeIndex < task.assignees.length; assigneeIndex++)
        {
            if (parseInt(task.assignees[assigneeIndex].id) === this.perms.user_id)
                return true;
        }

        return false;
    }

    TasksPermissionChecker.prototype.CanEditMyOwnTask = function(task) {
        if (!this.perms.can_edit_own_task)
            return false;

        if (parseInt(task.created_by.id) === this.perms.user_id)
            return true;

        for (var assigneeIndex = 0; assigneeIndex < task.assignees.length; assigneeIndex++)
        {
            if (parseInt(task.assignees[assigneeIndex].id) === this.perms.user_id)
                return true;
        }

        return false;
    }

    TasksPermissionChecker.prototype.CanUpdateMyTaskStatus = function(task, listId) {
        if (this.CanEditMyOwnTask(task))
            return true;

        var myTaskInSameList = this.IsMyTask(task) && (listId === parseInt(task.section_id));
        return this.perms.can_update_own_task_status && myTaskInSameList;
    }

    return TasksPermissionChecker;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));


/***/ }),

/***/ "./js/project/tasks.service.js":
/***/ (function(module, exports) {

(function()
{
    var moduleName = 'claromentis.project';

    angular.module(moduleName)
        .service('tasksService', tasksService);

    tasksService.$inject = ['$rootScope', '$http', 'growl.service', 'projectPreloadFactory', 'TaskSection', 'Task'];
    function tasksService($rootScope, $http, growlService, preload, TaskSection, Task) {
        var service = this;

        // Constant to mean 'unsorted' at the end of the list
        const DISPLAY_ORDER_NONE = 1000000;

        const STATUS_ID_TODO = -1;
        const STATUS_ID_COMPLETED = -2;

        const TASK_ENDPOINT = '/api/projects/v3/task/{task_id}';
        const TASK_ADD_ENDPOINT = '/api/projects/v3/task/add';
        const STATUS_ENDPOINT = '/api/projects/v3/{project_id}/statuses';
        const LIST_ENDPOINT = '/api/projects/v3/task/section';

        service.tasksUpdating = false;

        service.getTaskEndpoint = function(task_id)
        {
            return TASK_ENDPOINT.replace(/({task_id})/g, task_id);
        }

        service.getTaskAddEndpoint = function()
        {
            return TASK_ADD_ENDPOINT;
        }

        service.getStatusEndpoint = function(project_id)
        {
            return STATUS_ENDPOINT.replace(/({project_id})/g, project_id);
        }

        service.getListEndpoint = function()
        {
            return LIST_ENDPOINT;
        }

        service.perms = preload.perms;
        service.project_id = preload.id;
        service.lists = [];
        for (var section_index = 0; section_index < preload.lists.length; section_index++)
        {
            var section = new TaskSection();
            section.loadFromArray(preload.lists[section_index]);
            service.lists.push(section);
        }

        service.tasks = [];
        for (var task_index=0; task_index < preload.tasks.length; task_index++)
        {
            var task = new Task();
            task.loadFromArray(preload.tasks[task_index]);

            task.project_id = project_id;
            service.tasks.push(task);
        }

        service.statuses = [];
        for (var statusIndex = 0; statusIndex < preload.statuses.length; statusIndex++)
        {
            service.statuses.push(preload.statuses[statusIndex]);
        }

        // ID to use for new statuses, decrements each time one is added
        service.nextFreeStatusId = -10;

        service.canEditStatuses = function()
        {
            return service.perms.can_edit_project;
        };

        service.canEditTasks = function()
        {
            return service.perms.can_edit_any_task;
        };

        service.collapseList = function(listId)
        {
            for (var sectionIndex = 0; sectionIndex < service.lists.length; sectionIndex++)
            {
                if (service.lists[sectionIndex].id === listId)
                    service.lists[sectionIndex].expanded = false;
            }
        };

        service.expandList = function(listId)
        {
            for (var sectionIndex = 0; sectionIndex < service.lists.length; sectionIndex++)
            {
                if (service.lists[sectionIndex].id === listId)
                    service.lists[sectionIndex].expanded = true;
            }
        };

        service.saveStatus = function(status)
        {
            var data = {
                name: status.localized_name,
                sort_order: status.sort_order
            };

            var promise = null;
            var url = '';
            if (status.id < STATUS_ID_COMPLETED)
            {
                url = service.getStatusEndpoint(service.project_id);
                promise = $http.post(url, data);
            } else
            {
                url = service.getStatusEndpoint(service.project_id) + '/' + status.id;
                promise = $http.put(url, data);
            }

            service.tasksUpdating = true;
            promise.then(function(){
                service.tasksUpdating = false;
            }, function(){
                service.tasksUpdating = false;
            });
            return promise;
        };

        service.renameStatus = function(statusId, newName)
        {
            var oldName = '';
            var status = {};
            for (var statusIndex = 0; statusIndex < service.statuses.length; statusIndex++)
            {
                if (service.statuses[statusIndex].id === statusId)
                {
                    oldName = service.statuses[statusIndex].localized_name;
                    service.statuses[statusIndex].localized_name = newName;
                    status = service.statuses[statusIndex];
                }
            }

            // Don't do anything more if there was no change made
            if (newName === oldName)
                return;

            service.saveStatus(status).then(
                function(){
                    growlService.showSuccess(lmsg('projects.task.status.updated', newName));
                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data){
                    status.localized_name = oldName
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.addStatusBefore = function(sortOrder)
        {
            var name = lmsg('projects.task.status.default_name');
            var newStatus = {
                id: service.nextFreeStatusId,
                localized_name: name,
                name: name,
                sort_order: sortOrder,
                isNew: true
            };

            service.saveStatus(newStatus).then(
                function(data) {
                    service.statuses.length = 0;
                    for (var i = 0; i < data.data.statuses.length; i++)
                    {
                        var status = data.data.statuses[i];
                        if (status.id === data.data.status.id)
                            status.isNew = true;

                        service.statuses.push(status);
                    }

                    service.nextFreeStatusId--;
                    growlService.showSuccess(lmsg('projects.task.status.added', name));
                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data){
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );

            $rootScope.$broadcast('task_list_data.updated');
        }

        service.addStatusAfter = function(sortOrder)
        {
            var name = lmsg('projects.task.status.default_name');
            var newStatus = {
                id: service.nextFreeStatusId,
                localized_name: name,
                name: name,
                sort_order: sortOrder + 1,
                isNew: true
            };

            service.saveStatus(newStatus).then(
                function(data){
                    service.statuses.length = 0;
                    for (var i = 0; i < data.data.statuses.length; i++)
                    {
                        var status = data.data.statuses[i];
                        if (status.id === data.data.status.id)
                            status.isNew = true;

                        service.statuses.push(status);
                    }

                    service.nextFreeStatusId--;
                    growlService.showSuccess(lmsg('projects.task.status.added', name));
                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data){
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.deleteStatus = function(status)
        {
            var url = service.getStatusEndpoint(service.project_id) + '/' + status.id;
            service.tasksUpdating = true;
            $http.delete(url).then(
                function(data) {
                    service.statuses.length = 0;
                    for (var i = 0; i < data.data.statuses.length; i++)
                    {
                        service.statuses.push(data.data.statuses[i]);
                    }
                    service.tasksUpdating = false;
                    growlService.showSuccess(lmsg('projects.task.status.deleted', status.localized_name));
                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data) {
                    service.tasksUpdating = false;
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.updateStatusOrder = function(statusId, newOrder)
        {
            var status = {
                id: 0,
                localized_name: '',
                sort_order: newOrder
            };
            for (var statusIndex = 0; statusIndex < service.statuses.length; statusIndex++)
            {
                if (service.statuses[statusIndex].id === statusId)
                {
                    status.id = service.statuses[statusIndex].id;
                    status.localized_name = service.statuses[statusIndex].localized_name;
                }
            }

            service.saveStatus(status).then(
                function(data) {
                    service.statuses.length = 0;
                    for (var i = 0; i < data.data.statuses.length; i++)
                    {
                        service.statuses.push(data.data.statuses[i]);
                    }

                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data) {
                    // Irrelevant change to force watches to fire and rebuild local data using the original
                    // data, undoing the drag+drop
                    service.statuses[0].sort_order += 0.001;

                    $rootScope.$broadcast('task_list_data.updated');
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.saveList = function(list)
        {
            var data = {
                title: list.title,
                display_order: list.display_order,
                colour: list.colour,
                project_id: list.project_id
            };

            var promise = null;
            var url = '';
            if (list.id <= 0)
            {
                url = service.getListEndpoint(service.project_id) + '/add';
                promise = $http.post(url, data);
            } else
            {
                url = service.getListEndpoint(service.project_id) + '/' + list.id;
                promise = $http.put(url, data);
            }

            service.tasksUpdating = true;
            promise.then(function(){
                service.tasksUpdating = false;
            }, function(){
                service.tasksUpdating = false;
            });
            return promise;
        };

        service.editList = function(data)
        {
            return new Promise(function(resolve, reject) {
                var oldName = '';
                var oldColour = '';
                var list = {};

                if (data.id === 0)
                {
                    list = {
                        id: 0,
                        title: data.name,
                        colour: data.colour,
                        project_id: service.project_id,
                        display_order: service.lists.length
                    };
                } else
                {
                    for (var listIndex = 0; listIndex < service.lists.length; listIndex++)
                    {
                        if (service.lists[listIndex].id == data.id)
                        {
                            oldName = service.lists[listIndex].title;
                            oldColour = service.lists[listIndex].colour;
                            service.lists[listIndex].title = data.name;
                            service.lists[listIndex].colour = data.colour;
                            list = service.lists[listIndex];
                        }
                    }
                }

                // Don't do anything more if there was no change made
                if ((data.name === oldName) &&
                    (data.colour === oldColour))
                {
                    resolve();
                    return null;
                }

                var newName = data.name;

                service.saveList(list).then(
                    function(data){
                        growlService.showSuccess(lmsg('projects.task.list.updated', newName));

                        if (list.id === 0)
                        {
                            list = new TaskSection();
                            list.id = data.data.id;
                            list.colour = data.data.colour;
                            list.project_id = data.data.project_id;
                            list.display_order = data.data.display_order;
                            list.title = data.data.title;

                            service.lists.push(list);
                        }

                        $rootScope.$broadcast('task_list_data.updated');
                        resolve();
                    },
                    function(data){
                        list.title = oldName
                        list.colour = oldColour;
                        growlService.showError(lmsg('common.general.error'), data.data.error);

                        reject();
                    }
                );
            });
        };

        service.deleteList = function(list)
        {
            service.tasksUpdating = true;
            var url = service.getListEndpoint(service.project_id) + '/' + list.listId;
            $http.delete(url).then(
                function(data) {
                    var oldSections = service.lists;
                    service.lists.length = 0;
                    for (var key in data.data.sections)
                    {
                        if (data.data.sections.hasOwnProperty(key))
                        {
                            var sectionData = data.data.sections[key];
                            var section = null;

                            // Copy from existing data where possible to retain temporary state (collapsed, etc)
                            for (var j = 0; j < oldSections.length; j++)
                            {
                                if (sectionData.id === oldSections[j].id)
                                    section = oldSections[j];
                            }
                            if (section === null)
                                section = new TaskSection();

                            section.loadFromArray(sectionData);
                            service.lists.push(section);
                        }
                    }

                    service.tasksUpdating = false;
                    growlService.showSuccess(lmsg('projects.task.status.deleted', list.listName));
                    $rootScope.$broadcast('task_list_data.updated');

                    // trigger event about deleted section
                    angular.element('#headerButtonsApp').trigger('onDeleteList', {});
                },
                function(data) {
                    service.tasksUpdating = false;
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.updateListOrder = function(listId, newIndex)
        {
            // Find the right list that got moved
            var foundList = null;
            var foundIndex = null;
            for (var listIndex = 0; listIndex < service.lists.length; listIndex++)
            {
                if (service.lists[listIndex].id == listId)
                {
                    foundList = service.lists[listIndex];
                    foundIndex = listIndex;
                }
            }

            if (foundList !== null)
            {
                // Remove old list and reinsert it at the right place
                service.lists.splice(foundIndex, 1);
                service.lists.splice(newIndex, 0, foundList);
            }

            // Get new ID order
            var newOrder = []
            for (var listIndex = 0; listIndex < service.lists.length; listIndex++)
            {
                newOrder.push(service.lists[listIndex].id);
            }

            service.saveListOrder(newOrder).then(
                function(data) {
                    // Make a copy first to re-use old data from
                    var oldSections = [];
                    for (var listIndex = 0; listIndex < service.lists.length; listIndex++)
                    {
                        oldSections.push(service.lists[listIndex]);
                    }

                    service.lists.length = 0;
                    for (var key in data.data.sections)
                    {
                        if (data.data.sections.hasOwnProperty(key))
                        {
                            var sectionData = data.data.sections[key];
                            var section = null;

                            // Copy from existing data where possible to retain temporary state (collapsed, etc)
                            for (var oldListIndex = 0; oldListIndex < oldSections.length; oldListIndex++)
                            {
                                if (sectionData.id == oldSections[oldListIndex].id)
                                    section = oldSections[oldListIndex];
                            }
                            if (section === null)
                                section = new TaskSection();

                            section.loadFromArray(sectionData);
                            service.lists.push(section);
                        }
                    }

                    $rootScope.$broadcast('task_list_data.updated');
                },
                function(data) {
                    // Irrelevant change to force watches to fire and rebuild local data using the original
                    // data, undoing the drag+drop
                    service.statuses[0].sort_order += 0.001;

                    $rootScope.$broadcast('task_list_data.updated');
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.saveListOrder = function(listOrder)
        {
            var data = {
                project_id: service.project_id,
                newOrder: listOrder
            };

            var url = service.getListEndpoint() + '/order';
            var promise = $http.post(url, data);

            service.tasksUpdating = true;
            promise.then(function(){
                service.tasksUpdating = false;
            }, function(){
                service.tasksUpdating = false;
            });
            return promise;
        };

        service.saveTask = function(task)
        {
            var data = {
                title: task.title,
                description: task.description,
                orders: task.orders,
                project_id: task.project_id,
                assignees: task.assignees,
                due_date: task.due_date,
                priority: task.priority,
                duration: task.duration,
                is_private: task.is_private,
                section_id: task.list_id,
                status_id: task.status_id
            };

            var promise = null;
            var url = '';
            if (task.id <= 0)
            {
                url = service.getTaskAddEndpoint();
                promise = $http.post(url, data);
            } else
            {
                url = service.getTaskEndpoint(task.id);
                promise = $http.put(url, data);
            }

            service.tasksUpdating = true;
            promise.then(function(){
                service.tasksUpdating = false;
            }, function(){
                service.tasksUpdating = false;
            });
            return promise;
        };

        service.editTask = function(data, listType)
        {
            if (listType === undefined)
                listType = 'list';

            return new Promise(function(resolve, reject) {
                var oldTitle = '';
                var oldDescription = '';
                var oldAssignees = [];
                var oldDueDate = '';
                var oldPriority = '';
                var oldDuration = '';
                var oldIsPrivate = false;
                var oldListId = 0;
                var oldOrders;

                var newTask = {};

                if (data.id === 0)
                {
                    newTask = {
                        id: 0,
                        project_id: data.project_id,
                        title: data.name,
                        description: data.description,
                        assignees: data.assignees,
                        due_date: data.due_date,
                        priority: data.priority,
                        duration: data.duration,
                        is_private: data.is_private,
                        list_id: data.list_id,
                        orders: {
                            list: DISPLAY_ORDER_NONE,
                            board: DISPLAY_ORDER_NONE,
                            board_no_list: DISPLAY_ORDER_NONE,
                            my_tasks: DISPLAY_ORDER_NONE
                        }
                    };
                    newTask.orders[listType] = service.tasks.length;
                } else
                {
                    for (var taskIndex = 0; taskIndex < service.tasks.length; taskIndex++)
                    {
                        if (service.tasks[taskIndex].id == data.id)
                        {
                            oldTitle = service.tasks[taskIndex].title;
                            oldDescription = service.tasks[taskIndex].description;
                            oldAssignees = service.tasks[taskIndex].assignees;
                            oldDueDate = service.tasks[taskIndex].due_date;
                            oldPriority = service.tasks[taskIndex].priority;
                            oldDuration = service.tasks[taskIndex].duration;
                            oldIsPrivate = service.tasks[taskIndex].is_private;
                            oldListId = service.tasks[taskIndex].list_id;
                            oldOrders = service.tasks[taskIndex].orders;

                            service.tasks[taskIndex].title = data.name;
                            service.tasks[taskIndex].description = data.description;
                            service.tasks[taskIndex].assignees = data.assignees;
                            service.tasks[taskIndex].due_date = data.due_date;
                            service.tasks[taskIndex].priority = data.priority;
                            service.tasks[taskIndex].duration = data.duration;
                            service.tasks[taskIndex].is_private = data.is_private;
                            service.tasks[taskIndex].list_id = data.list_id;
                            service.tasks[taskIndex].orders = data.orders;

                            newTask = service.tasks[taskIndex];
                        }
                    }
                }

                // Don't do anything more if there was no change made
                if ((data.name === oldTitle) &&
                    (data.description === oldDescription) &&
                    (data.assignees === oldAssignees) &&
                    (data.due_date === oldDueDate) &&
                    (data.priority === oldPriority) &&
                    (data.duration === oldDuration) &&
                    (data.is_private === oldIsPrivate) &&
                    (data.list_id === oldListId) &&
                    (data.orders === oldOrders))
                {
                    resolve();
                    return null;
                }

                var newName = data.name;

                service.saveTask(newTask).then(
                    function(data){
                        growlService.showSuccess(lmsg('projects.task.task_updated', newName));

                        /**
                         * This is specified case when user removed all task list and API will create a default list for
                         * a new created task and here we have to update the front end.
                         */
                        if (isNaN(newTask.list_id))
                        {
                            list = new TaskSection();
                            list.id = data.data.section_id;
                            list.colour = '#ccddee';
                            list.project_id = service.project_id;
                            list.display_order = 0;
                            list.title = data.data.section_name;
                            service.lists.push(list);

                            $rootScope.$broadcast('task_list_data.updated');
                        }

                        if (newTask.id === 0)
                        {
                            task = new Task();
                            task.loadFromArray(data.data);

                            service.tasks.push(task);
                            $rootScope.$broadcast('task_list_data.updated');
                        }

                        resolve();
                    },
                    function(data){
                        if (newTask.id > 0)
                        {
                            task.title = oldTitle;
                            task.description = oldDescription;
                            task.assignees = oldAssignees;
                            task.due_date = oldDueDate;
                            task.priority = oldPriority;
                            task.duration = oldDuration;
                            task.is_private = oldIsPrivate;
                            task.list_id = oldListId;
                            task.orders = oldOrders;
                        }

                        growlService.showError(lmsg('common.general.error'), data.data.error);

                        reject();
                    }
                );
            });
        };

        service.updateTaskOrder = function(taskId, data)
        {
            service.saveTaskOrder(taskId, data).then(
                function(data)
                {
                    service.tasks.length = 0;
                    for (var i = 0; i < data.data.tasks.length; i++)
                    {
                        var task = new Task();
                        task.loadFromArray(data.data.tasks[i]);
                        service.tasks.push(task);
                    }

                    $rootScope.$broadcast('task_list_data.updated');
                }, function(data)
                {
                    growlService.showError(lmsg('common.general.error'), data.statusText);
                }
            );
        };

        service.saveTaskOrder = function(taskId, data)
        {
            data.projectId = service.project_id;

            var url = service.getTaskEndpoint(taskId) + '/order';
            var promise = $http.post(url, data);

            service.tasksUpdating = true;
            promise.then(function(){
                service.tasksUpdating = false;
            }, function(){
                service.tasksUpdating = false;
            });
            return promise;
        };

        service.deleteTask = function(taskId)
        {
            // The task has already been deleted by the edit task modal so just remove it locally
            var oldTasks = [];
            for (var taskIndex = 0; taskIndex < service.tasks.length; taskIndex++)
            {
                oldTasks.push(service.tasks[taskIndex]);
            }

            service.tasks.length = 0;
            for (taskIndex = 0; taskIndex < oldTasks.length; taskIndex++)
            {
                if (oldTasks[taskIndex].id !== taskId)
                    service.tasks.push(oldTasks[taskIndex]);
            }

            $rootScope.$broadcast('task_list_data.updated');
        };

        service.toggleCompleted = function(taskId)
        {
            for (var taskIndex = 0; taskIndex < service.tasks.length; taskIndex++)
            {
                if (service.tasks[taskIndex].id === taskId)
                {
                    var statusId = service.tasks[taskIndex].status_id;

                    if (statusId === STATUS_ID_COMPLETED)
                        service.tasks[taskIndex].status_id = STATUS_ID_TODO;
                    else
                        service.tasks[taskIndex].status_id = STATUS_ID_COMPLETED;

                    service.saveTask(service.tasks[taskIndex]).then(function() {
                        $rootScope.$broadcast('task_list_data.updated');
                    }, function(data) {
                        growlService.showError(lmsg('common.general.error'), data.statusText);
                    });
                }
            }
        };

        angular.element('#headerButtonsApp').on('onAddList', function(event, data)
        {
            service.editList({
                id: 0,
                name: data.title,
                colour: data.colour
            }).then(function(){
                data.success();
            }, function(){
            });
        });

        angular.element('#headerButtonsApp').on('onAddTask', function(event, data)
        {
            service.editTask({
                id: 0,
                project_id: service.project_id,
                name: data.name,
                description: data.description,
                assignees: data.assignees,
                due_date: data.due_date,
                priority: data.priority,
                duration: data.duration,
                is_private: data.is_private,
                list_id: data.list_id,
                status_id: data.status_id
            }).then(function(){
                data.success();
            }, function(){
            });
        });
    }
}());


/***/ }),

/***/ "./js/project/tasks_board.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var TasksPermissionChecker = __webpack_require__("./js/project/tasks.permission_checker.class.js");

    var template = __webpack_require__("./html/project/tasks_board.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangProjectTasksBoard', projectTasksBoard);

    projectTasksBoard.$inject = ['tasksService'];
    function projectTasksBoard(service) {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                tasks: '<',
                statuses: '<',
                lists: '<',
                groupByList: '<',
                perms: '<',
                isMobile: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller,
            link: link
        };

        function controller()
        {
            var ctrl = this;

            ctrl.tasksService = service;
            ctrl.todoStatus = null;
            ctrl.orderableStatuses = [];
            ctrl.doneStatus = null;

            this.$onInit = function() {
                ctrl.buildStatuses();
                ctrl.permsChecker = new TasksPermissionChecker(ctrl.perms);
            };

            ctrl.HasTaskDragPerms = function(task, listId) {
                if (ctrl.isMobile)
                    return false;

                return ctrl.permsChecker.HasTaskDragPerms(task, listId);
            };

            ctrl.HasListDragPerms = function() {
                if (ctrl.isMobile)
                    return false;

                return ctrl.permsChecker.HasListDragPerms();
            };

            ctrl.HasStatusDragPerms = function() {
                if (ctrl.isMobile)
                    return false;

                return ctrl.permsChecker.HasStatusDragPerms();
            };

            ctrl.IsMyTask = function(task) {
                return ctrl.permsChecker.IsMyTask(task);
            }

            ctrl.CanEditMyOwnTask = function(task) {
                return ctrl.permsChecker.CanEditMyOwnTask(task);
            }

            ctrl.CanUpdateMyTaskStatus = function(task, listId) {
                return ctrl.permsChecker.CanUpdateMyTaskStatus(task, listId);
            }

            ctrl.buildStatuses = function()
            {
                var taskCounts = {};
                for (var listIndex = 0; listIndex < ctrl.tasks.lists.length; listIndex++)
                {
                    if (ctrl.tasks.lists[listIndex].statuses !== undefined)
                    {
                        var list = ctrl.tasks.lists[listIndex];
                        for (var statusIndex = 0; statusIndex < list.statuses.length; statusIndex++)
                        {
                            var status = list.statuses[statusIndex];

                            if (taskCounts[status.status.id] === undefined)
                                taskCounts[status.status.id] = 0;

                            taskCounts[status.status.id] += status.tasks.length;
                        }
                    }
                }

                ctrl.todoStatus = ctrl.statuses[0];
                ctrl.todoStatus.isNew = false;
                ctrl.orderableStatuses.length = 0;
                for (var i = 1; i < ctrl.statuses.length - 1; i++)
                {
                    ctrl.statuses[i].taskCount = taskCounts[ctrl.statuses[i].id];
                    ctrl.orderableStatuses.push(ctrl.statuses[i]);
                }

                ctrl.doneStatus = ctrl.statuses[ctrl.statuses.length - 1];
                ctrl.doneStatus.isNew = false;
            }

            /**
             * Called while dragging to determine where items can be dropped. Because we have both task and section containers we find
             * the types involved and limit items to only be dropped in a container of their own kind. If this is not done we can drop
             * tasks as a section and vice versa.
             */
            ctrl.dragAccept = function (sourceItemHandleScope, destSortableScope)
            {
                var sourceType = '';
                if ('task' in sourceItemHandleScope.itemScope.$parent)
                    sourceType = 'Task';
                else if ('list' in sourceItemHandleScope.itemScope.$parent)
                    sourceType = 'Section';
                else if ('status' in sourceItemHandleScope.itemScope.$parent)
                    sourceType = 'Status';

                var destType = destSortableScope.options.dropType;

                if (sourceType === destType)
                {
                    if ((sourceType === 'Section') &&
                        (!ctrl.perms.can_edit_any_task))
                    {
                        return false;
                    }

                    if (sourceType === 'Task')
                    {
                        if (ctrl.perms.can_edit_any_task)
                            return true;

                        var task = sourceItemHandleScope.itemScope.$parent.task;

                        if (ctrl.CanEditMyOwnTask(task))
                            return true;

                        if (ctrl.CanUpdateMyTaskStatus(task, destSortableScope.element.data('list-id')))
                            return true;

                        // Can only drag in the circumstances above
                        return false;
                    }

                    // Other combos are fine
                    return true;
                }

                // Can't drag between types at all
                return false;
            };

            ctrl.statusOrderChanged = function(data)
            {
                service.updateStatusOrder(data.source.itemScope.$parent.status.id, ctrl.statuses[data.dest.index + 1].sort_order);
            };

            ctrl.sectionOrderChanged = function(data)
            {
                service.updateListOrder(data.source.itemScope.$parent.list.list.id, data.dest.index);
            };


            ctrl.taskOrderChanged = function(data)
            {
                var destStatus = data.dest.sortableScope.$parent.$parent.ctrl.status;
                var destList = data.dest.sortableScope.$parent.$parent.ctrl.list;

                var taskId = parseInt(destStatus.tasks[data.dest.index].id);

                var newOrder = [];
                for (var taskIndex = 0; taskIndex < destStatus.tasks.length; taskIndex++)
                {
                    newOrder.push(parseInt(destStatus.tasks[taskIndex].id));
                }

                var newData = {
                    statusId: parseInt(destStatus.status.id),
                    order: newOrder
                };

                if (destList !== undefined)
                    newData.listId = parseInt(destList.list.id);

                service.updateTaskOrder(taskId, newData);
            };

            ctrl.dragStatusListeners =
            {
                dropType: 'Status',
                accept: ctrl.dragAccept,
                orderChanged: ctrl.statusOrderChanged,
                containment: ".js-drag-container",
                containerPositioning: 'relative'
            };

            ctrl.dragSectionListeners =
            {
                dropType: 'Section',
                accept: ctrl.dragAccept,
                orderChanged: ctrl.sectionOrderChanged,
                containment: ".js-drag-container",
                containerPositioning: 'relative'
            };

            ctrl.dragTaskListeners =
            {
                dropType: 'Task',
                accept: ctrl.dragAccept,
                itemMoved: ctrl.taskOrderChanged,
                orderChanged: ctrl.taskOrderChanged,
                containment: '.js-drag-container',
                containerPositioning: 'relative'
            };
        }

        function link($scope, $element, $attrs, $ctrl)
        {
            // Rebuild the statuses headers due to change of data
            $scope.$watch('ctrl.statuses', function(newVal, oldVal){
                $ctrl.buildStatuses();
            }, true);

            $scope.$watch('ctrl.tasks', function(newVal, oldVal){
                $ctrl.buildStatuses();
            }, true);
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks_board_section.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/project/tasks_board_section.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangTasksBoardSection', tasksBoardSection);

    tasksBoardSection.$inject = ['$timeout', 'tasksService', 'confirmService'];
    function tasksBoardSection($timeout, service, confirmService) {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                list: '<',
                allowDrag: '<'
            },
            transclude: true,
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.editName = '';
            ctrl.editColour = '';
            ctrl.editMode = false;
            ctrl.expanded = true;
            ctrl.hasEditPerms = service.canEditTasks();

            this.$onInit = function() {
                // For new lists, jump straight to editing the name
                if (ctrl.list.isNew !== undefined && ctrl.list.isNew)
                    ctrl.startRename();
            };

            ctrl.collapseList = function(listId)
            {
                service.collapseList(listId);
            }

            ctrl.expandList = function(listId)
            {
                service.expandList(listId);
            }

            ctrl.startEdit = function($event) {
                if ($event !== undefined)
                    $event.preventDefault();

                ctrl.editName = ctrl.list.listName;
                ctrl.editColour = ctrl.list.list.colour;
                ctrl.editMode = true;

                $timeout(function() {
                    angular.element('#list_' + ctrl.list.listId).focus();
                }, 0);
            };

            ctrl.cancelEdit = function($event) {
                $event.preventDefault();

                ctrl.editMode = false;
                ctrl.list.isNew = false;
            };

            ctrl.edit = function($event) {
                if ($event !== undefined)
                    $event.preventDefault();

                service.editList({
                    id: ctrl.list.listId,
                    name: ctrl.editName,
                    colour: ctrl.editColour
                });
                ctrl.editMode = false;
                ctrl.list.isNew = false;
            };

            ctrl.delete = function($event) {
                if ($event !== undefined)
                    $event.preventDefault();

                confirmService.show({
                    title: lmsg('projects.tasks.delete_list.title'),
                    message: lmsg('projects.tasks.delete_list.message', ctrl.list.listName)
                }).then(function() {
                    service.deleteList(ctrl.list);
                }, function() {
                });
            };

            ctrl.noDrag = function()
            {
                return ctrl.allowDrag === false ? true : undefined;
            };
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks_board_status.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/project/tasks_board_status.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangTasksBoardStatus', tasksBoardStatus);

    function tasksBoardStatus() {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                status: '<',
                list: '<'
            },
            transclude: true,
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks_board_status_header.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/project/tasks_board_status_header.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangTasksBoardStatusHeader', tasksBoardStatusHeader);

    tasksBoardStatusHeader.$inject = ['$timeout', 'tasksService', 'confirmService'];
    function tasksBoardStatusHeader($timeout, service, confirmService) {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                status: '<',
                taskCount: '<',
                allowDrag: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.editName = '';
            ctrl.editMode = false;
            ctrl.hasEditPerms = service.canEditStatuses();

            ctrl.$onInit = function() {
                // For new statuses, jump straight to editing the name
                if (ctrl.status.isNew !== undefined && ctrl.status.isNew)
                    ctrl.startRename();
            };

            ctrl.isDraggable = function()
            {
                if (!ctrl.allowDrag)
                    return false;

                if (typeof ctrl.status.id !== 'number')
                    return false;

                if ((ctrl.status.id === -1) || (ctrl.status.id === -2))
                    return false;

                return true;
            };

            ctrl.startRename = function($event) {
                if ($event !== undefined)
                    $event.preventDefault();

                ctrl.editName = ctrl.status.localized_name;
                ctrl.editMode = true;

                $timeout(function() {
                    angular.element('#status_header_' + ctrl.status.id).focus();
                }, 0);
            };

            ctrl.cancelRename = function($event) {
                $event.preventDefault();

                ctrl.editMode = false;
                ctrl.status.isNew = false;
            };

            ctrl.rename = function($event) {
                if ($event !== undefined)
                    $event.preventDefault();

                service.renameStatus(ctrl.status.id, ctrl.editName);
                ctrl.editMode = false;
                ctrl.status.isNew = false;
            };

            ctrl.addStatusBefore = function($event)
            {
                $event.preventDefault();

                service.addStatusBefore(ctrl.status.sort_order);
            };

            ctrl.addStatusAfter = function($event)
            {
                $event.preventDefault();

                service.addStatusAfter(ctrl.status.sort_order);
            };

            ctrl.delete = function($event)
            {
                $event.preventDefault();

                if (ctrl.taskCount === 0)
                {
                    confirmService.show({
                        title: lmsg('projects.tasks.delete_status.title'),
                        message: lmsg('projects.tasks.delete_status.message', ctrl.status.localized_name)
                    }).then(function() {
                        service.deleteStatus(ctrl.status);
                    }, function() {});
                } else
                {
                    confirmService.show({
                        title: lmsg('projects.tasks.delete_status.title'),
                        message: lmsg('projects.tasks.delete_status.remove_tasks_message', ctrl.status.localized_name),
                        icon: 'list-alt',
                        mode: 'ok'
                    }).then(function() {}, function() {});
                }
            };
        }
    }
}());


/***/ }),

/***/ "./js/project/tasks_list.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var TasksPermissionChecker = __webpack_require__("./js/project/tasks.permission_checker.class.js");

    var template = __webpack_require__("./html/project/tasks_list.directive.html");
	var moduleName = 'claromentis.project';

	angular.module(moduleName)
        .directive('clangProjectTasksList', projectTasksList);

	projectTasksList.$inject = ['Task', 'tasksService'];
    function projectTasksList(Task, service) {
        return {
            restrict: 'E',
            templateUrl: template,
            scope: {
                tasks: '<',
                perms: '<',
                isMobile: '<'
            },
            controllerAs: 'ctrl',
            bindToController: true,
            controller: controller
        };

        function controller()
        {
            var ctrl = this;

            ctrl.$onInit = function() {
                ctrl.permsChecker = new TasksPermissionChecker(ctrl.perms);
            }

            ctrl.tasksService = service;

            ctrl.HasTaskDragPerms = function(task, listId) {
                if (ctrl.isMobile)
                    return false;

                return ctrl.permsChecker.HasTaskDragPerms(task, listId);
            };

            ctrl.HasListDragPerms = function() {
                if (ctrl.isMobile)
                    return false;

                return ctrl.permsChecker.HasListDragPerms();
            };

            ctrl.IsMyTask = function(task) {
                return ctrl.permsChecker.IsMyTask(task);
            }

            ctrl.CanEditMyOwnTask = function(task) {
                return ctrl.permsChecker.CanEditMyOwnTask(task);
            }

            ctrl.CanUpdateMyTaskStatus = function(task, listId) {
                return ctrl.permsChecker.CanUpdateMyTaskStatus(task, listId);
            }

            /**
             * Called while dragging to determine where items can be dropped. Because we have both task and section containers we find
             * the types involved and limit items to only be dropped in a container of their own kind. If this is not done we can drop
             * tasks as a section and vice versa.
             */
            ctrl.dragAccept = function (sourceItemHandleScope, destSortableScope)
            {
                var sourceType = '';
                if ('task' in sourceItemHandleScope.itemScope.$parent)
                    sourceType = 'Task';
                else if ('list_entry' in sourceItemHandleScope.itemScope.$parent)
                    sourceType = 'Section';

                var destType = destSortableScope.options.dropType;

                if (sourceType === destType)
                {
                    if ((sourceType === 'Section') &&
                        (!ctrl.perms.can_edit_any_task))
                    {
                        return false;
                    }

                    if (sourceType === 'Task')
                    {
                        if (ctrl.perms.can_edit_any_task)
                            return true;

                        var task = sourceItemHandleScope.itemScope.$parent.task;

                        if (ctrl.CanEditMyOwnTask(task))
                            return true;

                        if (ctrl.CanUpdateMyTaskStatus(task, destSortableScope.element.data('list-id')))
                            return true;

                        // Can only drag in the circumstances above
                        return false;
                    }

                    // Other combos are fine
                    return true;
                }

                // Can't drag between types at all
                return false;
            };

            ctrl.taskOrderChanged = function(data)
            {
                var destList = data.dest.sortableScope.$parent.$parent.ctrl.list;

                var taskId = parseInt(destList.tasks[data.dest.index].id);

                var newOrder = [];
                for (var taskIndex = 0; taskIndex < destList.tasks.length; taskIndex++)
                {
                    newOrder.push(parseInt(destList.tasks[taskIndex].id));
                }

                var newData = {
                    listId: parseInt(destList.list.id),
                    order: newOrder
                };

                service.updateTaskOrder(taskId, newData);
            };

            ctrl.sectionOrderChanged = function(data)
            {
                service.updateListOrder(data.source.itemScope.$parent.list_entry.list.id, data.dest.index);
            };

            ctrl.dragTaskListeners =
            {
                dropType: 'Task',
                accept: ctrl.dragAccept,
                itemMoved: ctrl.taskOrderChanged,
                orderChanged: ctrl.taskOrderChanged,
                containment: '.js-drag-container',
                containerPositioning: 'relative'
            };

            ctrl.dragSectionListeners =
            {
                dropType: 'Section',
                accept: ctrl.dragAccept,
                orderChanged: ctrl.sectionOrderChanged,
                containment: '.js-drag-container',
                containerPositioning: 'relative'
            };

            ctrl.taskDeletedCallback = function(task)
            {
                service.deleteTask(task.id)
            };

            ctrl.taskCompleteToggledCallback = function(task)
            {
                service.toggleCompleted(task.id);
            };
        }
    }
}());


/***/ }),

/***/ "./js/projects.detail.js":
/***/ (function(module, exports, __webpack_require__) {

var angular = __webpack_require__(1);
window.angular = angular;

__webpack_require__(2);
__webpack_require__(9);
__webpack_require__(5);
__webpack_require__(7);
__webpack_require__(4);
__webpack_require__(0);
__webpack_require__(6);
__webpack_require__("./js/lib/ng-sortable.js");
__webpack_require__(5);
__webpack_require__(10);

__webpack_require__("./js/projects.tasks_util.js");

angular.module('claromentis.project', []);
__webpack_require__("./js/directive/colourPicker.js");
__webpack_require__("./js/directive/datePicker.js");


// services
angular.module('cla.service.project_details', []);
__webpack_require__("./js/service/projectDetails.service.js");

angular.module('cla.service.project_task_list', []);
__webpack_require__("./js/service/projectTaskList.service.js");
__webpack_require__("./js/service/history.service.js");

// Other modules
__webpack_require__("./js/ng/modules/widgets/projects_widgets.module.js");
__webpack_require__("./js/ng/modules/project/header_buttons/header_buttons.module.js");

function sanitizeFilter($sce)
{
    return function(htmlCode)
    {
        return $sce.trustAsHtml(htmlCode);
    }
}

angular.module(
    'projectDetailApp',
    [
        'ui.bootstrap', 'ui.sortable', 'ngResource', 'angularUtils.directives.dirPagination', 'claromentis.project',
        'commentsModule', 'cla.core.lmsg', 'cla.core.breadcrumb', 'cla.core.growl', 'cla.core.datatable', 'cla.core.html_editor',
        'topicsPreloadModule', 'cla.service.project_details', 'claromentis.projects.widgets',
        'claromentis.project.tasks_util'
    ]
).filter("sanitize", ['$sce', sanitizeFilter]);

__webpack_require__("./js/model/projectTaskSectionModel.js");

__webpack_require__("./js/controller/projectDetailCtrl.js");
__webpack_require__("./js/controller/projectTasksCtrl.js");
__webpack_require__("./js/controller/projectTeamCtrl.js");
__webpack_require__("./js/controller/projectFilesCtrl.js");
__webpack_require__("./js/controller/historyCtrl.js");

__webpack_require__("./js/directive/history_changes.directive.js");
__webpack_require__("./js/directive/history_user.directive.js");
__webpack_require__("./js/directive/history_priority.directive.js");
__webpack_require__("./js/directive/history_is_private.directive.js");
__webpack_require__("./js/directive/history_status.directive.js");
__webpack_require__("./js/directive/history_list.directive.js");

__webpack_require__("./js/project/tasks.permission_checker.class.js");

__webpack_require__("./js/project/tasks.service.js");
__webpack_require__("./js/project/tasks.list_type.directive.js");
__webpack_require__("./js/project/tasks.directive.js");

__webpack_require__("./js/project/tasks_board.directive.js");
__webpack_require__("./js/project/tasks_board_section.directive.js");
__webpack_require__("./js/project/tasks_board_status_header.directive.js");
__webpack_require__("./js/project/tasks_board_status.directive.js");
__webpack_require__("./js/project/tasks_list.directive.js");

__webpack_require__("./js/directive/topics_table.directive.js");

__webpack_require__("./js/directive/nav_tabs.js");

__webpack_require__("./js/task/taskEdit.directive.js");
__webpack_require__("./js/task/taskAddButton.directive.js");
__webpack_require__("./js/task/taskEdit.service.js");

__webpack_require__("../js/kolorpicker/style/kolorpicker.css");

module.exports = null;


/***/ }),

/***/ "./js/projects.tasks_util.js":
/***/ (function(module, exports, __webpack_require__) {

angular.module('claromentis.project.tasks_util', ['claromentis.projects.widgets']);
__webpack_require__("./js/model/projectTaskModel.js");
__webpack_require__("./js/task/task.directive.js");


/***/ }),

/***/ "./js/service/history.service.js":
/***/ (function(module, exports) {

(function()
{
    var moduleName = 'cla.projects_history';

    angular.module(moduleName, [])
        .service('claProjectsHistoryService', ['historyPreloadFactory', projectHistoryService]);

    function projectHistoryService(historyPreloadFactory)
    {
        var service = this;

        service.statuses = historyPreloadFactory.statuses;
        service.lists = historyPreloadFactory.lists;

        service.getStatusName = function(status_id) {
            for (var statusIndex = 0; statusIndex < service.statuses.length; statusIndex++)
            {
                if (service.statuses[statusIndex].id === status_id)
                    return service.statuses[statusIndex].name;
            }

            return null;
        };

        service.getListName = function(list_id) {
            for (var listIndex = 0; listIndex < service.lists.length; listIndex++)
            {
                if (service.lists[listIndex].id === list_id)
                    return service.lists[listIndex].title;
            }

            return null;
        };
    }
}());


/***/ }),

/***/ "./js/service/projectDetails.service.js":
/***/ (function(module, exports) {

(function()
{
    var moduleName = 'cla.service.project_details';

    // Manual singleton as the .service() appears to return multiple instances across Angular apps
    var instance = new ProjectDetailsService();

    angular.module(moduleName)
        .service('claProjectDetailsService', function() {
            return instance;
        });

    function ProjectDetailsService()
    {
        var service = this;

        service.currentTab = 'overviewTab';

        service.tabCounts = {
            taskCount: 0,
            totalTaskCount: 0,
            teamCount: 0,
            topicCount: 0,
            fileCount: 0
        };

        service.callbacks = [];

        service.triggerCallbacks = function()
        {
            for (var callbackIndex = 0; callbackIndex < service.callbacks.length; callbackIndex++)
            {
                (service.callbacks[callbackIndex])({
                    tab: service.currentTab,
                    counts: service.tabCounts
                });
            }
        };

        service.setLocation = function(location)
        {
            service.currentTab = location;

            service.triggerCallbacks();
        };

        service.getLocation = function()
        {
            return service.currentTab;
        };

        service.registerCallback = function(callback)
        {
            service.callbacks.push(callback);
        };

        service.setTaskCount = function(count, totalCount) {
            service.tabCounts.taskCount = count;
            service.tabCounts.totalTaskCount = totalCount;

            service.triggerCallbacks();
        };

        service.setTeamCount = function(count) {
            service.tabCounts.teamCount = count;

            service.triggerCallbacks();
        };

        service.setTopicCount = function(count) {
            service.tabCounts.topicCount = count;

            service.triggerCallbacks();
        };

        service.setFileCount = function(count) {
            service.tabCounts.fileCount = count;

            service.triggerCallbacks();
        };
    }
}());


/***/ }),

/***/ "./js/service/projectTaskList.service.js":
/***/ (function(module, exports) {

(function()
{
    var moduleName = 'cla.service.project_task_list';

    angular.module(moduleName)
        .service('claTaskSectionService', projectTaskListService);

    projectTaskListService.$inject = ['$http'];

    function projectTaskListService($http)
    {
        var service = this;

        service.get = function (project_id) {
            return $http.get('/api/projects/v3/' + project_id +'/sections', {})
                .then(function (response) {
                    return response.data;
                }, function (err) {
                    console.error('Request failed', err);
                });
        }
    }
}());


/***/ }),

/***/ "./js/task/task.directive.js":
/***/ (function(module, exports, __webpack_require__) {

var task_template = __webpack_require__("./html/directives/task.directive.html");
var taskItemLoaded = taskItemLoaded || false;

(function()
{
	var moduleName = 'claromentis.project.tasks_util';
	var module = angular.module(moduleName);

	if (!taskItemLoaded)
	{
		module
			.directive('projectTaskItem', projectTaskItem);

        projectTaskItem.$inject = ['task_edit_service', 'userListService'];
		function projectTaskItem(taskEditService, userListService) {
			return {
				restrict: 'E',
				templateUrl: function(elem,attrs) {
					return attrs.templateUrl || task_template
				},
				scope: {
					task: '=',
                    showProjectName: '=',
                    showSectionName: '=',
                    currentUserId: '@',
					useDragAndDrop: '@',
                    display: '@',
					listPrefix: '@',
					taskUpdate: '&',
					taskDelete: '&',
					taskCompleteToggled: '&',
					titleLength: '@',
                    showAssignees: '<',
                    list: '<',
                    showSource: '@',
                    allowDrag: '<'
				},
				controllerAs: 'item',
				bindToController: true,
				controller: controller,
				link: link
			};

			function controller()
			{
				var ctrl = this;
                ctrl.showSource = !!ctrl.showSource;
				ctrl.$element = null;

                this.$onInit = function() {
                    ctrl.updateTitle();

                    // Set default if needed
                    if ((ctrl.showAssignees !== 'others') &&
                        (ctrl.showAssignees !== 'none'))
                    {
                        ctrl.showAssignees = 'all';
                    }

                    if (ctrl.currentUserId === undefined)
                        ctrl.currentUserId = 0;
                    else
                        ctrl.currentUserId = parseInt(ctrl.currentUserId);

                    // Default to allowing dragging if unspecified
                    if (ctrl.allowDrag === undefined)
                        ctrl.allowDrag = true;
                };

				ctrl.startTaskEdit = function(event, id)
				{
					event.preventDefault();
					angular.element('.js-task_edit_button_' + id).tooltip('hide');
					taskEditService.startEdit(ctrl.task, ctrl.$element);
				};

				ctrl.completeToggled = function()
				{
					ctrl.taskCompleteToggled({task: ctrl.task});
				};

				ctrl.taskUpdated = function(task)
				{
                    ctrl.taskUpdate({task: ctrl.task});

                    ctrl.task.id = task.id;
                    ctrl.task.title = task.title;
                    ctrl.task.description = task.description;
                    ctrl.task.project_id = task.project_id;
                    ctrl.task.assignees = task.assignees;
                    ctrl.task.due_date = task.due_date;
                    ctrl.task.priority = parseInt(task.priority);
                    ctrl.task.is_private = task.is_private;
                    ctrl.task.duration = task.duration;
                    ctrl.task.duration_units = task.duration_units;
                    ctrl.task.due_date_type = task.due_date_type;
                    ctrl.task.due_date_formatted = task.due_date_formatted;
                    ctrl.task.perms = task.perms;

					ctrl.updateTitle();
				};

				ctrl.updateTitle = function()
				{
					// Truncate the title if needed and requested
					var max_length = parseInt(ctrl.titleLength);
					if (typeof ctrl.titleLength === 'undefined')
						max_length = 0;
					ctrl.task.title_truncated = ctrl.task.title;
					if ((max_length !== 0) &&
						(ctrl.task.title.length > max_length))
					{
						ctrl.task.title_truncated = ctrl.task.title_truncated.substring(max_length, 0) + '…';
					}
				};

				ctrl.taskDeleted = function(task)
                {
                    if (ctrl.taskDelete !== undefined)
                        ctrl.taskDelete(task);
                };

                ctrl.getAssignees = function()
                {
                    if (ctrl.showAssignees === 'none')
                        return [];
                    if (ctrl.showAssignees === 'all')
                        return ctrl.task.assignees;

                    // Must be 'others' so strip out current user
                    var userId = parseInt(ctrl.currentUserId);
                    var assignees = [];
                    for (var assigneeIndex = 0; assigneeIndex < ctrl.task.assignees.length; assigneeIndex++)
                    {
                        if(ctrl.task.assignees[assigneeIndex].id !== userId)
                            assignees.push(ctrl.task.assignees[assigneeIndex]);
                    }

                    return assignees;
                };

				ctrl.noDrag = function()
                {
                    return ctrl.allowDrag === false ? true : undefined;
                };

                ctrl.additionalAssigneesClick = function() {
                    userListService.showUsersList(ctrl.task.assignees);
                };
			}

			function link(scope, element, attrs, controller)
			{
			    controller.$element = element;
			    controller.$element.on('cancelEdit', function(event){
                });
                controller.$element.on('editComplete', function(event, task){
                    controller.taskUpdated(task);
                });
                controller.$element.on('taskDeleted', function(event, task){
                    controller.taskDeleted(task);
                });

				// Default to showing all parts unless told otherwise
				controller.showEdit = (attrs.showEdit !== 'false');
				controller.showPrivate = (attrs.showPrivate !== 'false');
				controller.showDate = (attrs.showDate !== 'false');
				controller.showFiles = (attrs.showFiles !== 'false');
				controller.showComments = (attrs.showComments !== 'false');
			}
		}
	}

	taskItemLoaded = true;
}());


/***/ }),

/***/ "./js/task/taskAddButton.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/directives/taskAddButton.directive.html");

	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.directive('projectTaskAdd', projectTaskAdd);

	projectTaskAdd.$inject = ['$timeout', 'task_edit_service', 'Task'];
	function projectTaskAdd($timeout, service, Task)
	{
		return {
			restrict: 'E',
			templateUrl: template,
			scope: {
                sectionId: '=',
                projectId: '=',
                onTaskAdded: '&'
			},
			controllerAs: 'add',
			bindToController: true,
			controller: controller,
            link: link
		};

		function controller()
		{
			var ctrl = this;
			ctrl.$element = null;
            ctrl.task = null;

            ctrl.$onInit = function()
            {
                ctrl.id = 'js-task_add_button_' + ctrl.sectionId;
                ctrl.blankTask = new Task();
                ctrl.blankTask.project_id = parseInt(ctrl.projectId);
                ctrl.blankTask.section_id = parseInt(ctrl.sectionId);
            }

            ctrl.startTaskEdit = function(event, section_id)
            {
                ctrl.task = angular.copy(ctrl.blankTask);

                event.preventDefault();
                angular.element('.js-task_add_button_' + section_id).tooltip('hide');
                service.startEdit(ctrl.task, ctrl.$element);
            };

			ctrl.taskAdded = function(task)
            {
                ctrl.onTaskAdded({task: task});
            }
		}

        function link(scope, element, attrs, controller)
        {
            controller.$element = element;
            controller.$element.on('editComplete', function(event, task){
                controller.taskAdded(task);
            });
        }
	}
}());


/***/ }),

/***/ "./js/task/taskEdit.directive.js":
/***/ (function(module, exports, __webpack_require__) {

(function()
{
    var template = __webpack_require__("./html/directives/taskEdit.directive.html");

	var moduleName = 'claromentis.project';
	var module;

	try {
		module = angular.module(moduleName);
	} catch(err) {
		// named module does not exist, so create one
		module = angular.module(moduleName, []);
	}

	module
		.directive('projectTaskEdit', projectTaskEdit);

	projectTaskEdit.$inject = ['$timeout', 'task_edit_service', 'Task', 'confirmService'];
	function projectTaskEdit($timeout, service, Task, confirmService)
	{
		return {
			restrict: 'E',
			templateUrl: template,
			scope: {
    			taskDelete: '&',
				taskUpdate: '&'
			},
			controllerAs: 'edit',
			bindToController: true,
			controller: controller,
			link: link
		};

		function controller()
		{
			var ctrl = this;

			ctrl.task = new Task();
			ctrl.visible = false;

            this.$onInit = function() {
                ctrl.unique_id = ctrl.task.id;
            };

			// ctrl.userControl gives us an object shared with the user picker to allow us to run methods inside its scope (needed to reset Select2)
			ctrl.userControl = {};
			ctrl.htmlControl = {};

			ctrl.startTaskEdit = function()
			{
			    ctrl.visible = true;

			    // Need to let the modal content become visible in DOM before resetting it
			    $timeout(function() {
			        // Reset priority picker
				    ctrl.enableEditTaskPriority(ctrl.task);

                    // Reset the user picker if this is a new task.
                    if (ctrl.task.assignees === undefined)
                        ctrl.userControl.reset();

                    ctrl.htmlControl.onOpen();

                    ctrl.$modal.modal('show');
                }, 0);
			};

			ctrl.cancelTaskEdit = function(event)
			{
				ctrl.endTaskEdit(event);
			};

            ctrl.endTaskEdit = function(event)
            {
                event.preventDefault();

                ctrl.$modal.modal('hide');
                ctrl.visible = false;
            }

			ctrl.saveTaskEdit = function(event)
			{
				event.preventDefault();

				var task = ctrl.task;

				task = service.taskUpdated(task, function(){
				    if (ctrl.taskUpdate !== undefined)
                        ctrl.taskUpdate({task: task})

                    ctrl.$modal.modal('hide');
                });
			};

			ctrl.deleteTask = function(event)
			{
				event.preventDefault();

                confirmService.show({
                    title: lmsg('projects.task.delete_task.title'),
                    message: lmsg('projects.task.delete_confirm'),
                }).then(function() {
                    ctrl.task.delete(function()
                    {
                        if (ctrl.taskDelete !== undefined)
                            ctrl.taskDelete({task: ctrl.task});

                        ctrl.$modal.modal('hide');
                        service.taskDeleted(ctrl.task);
                    }, function(data)
                    {
                        cla.showMessage(data.error, '', true);
                    });
                }, function() {});
			};

			ctrl.enableEditTaskPriority = function()
			{
				angular.element('.js-task-edit-priority').each(function()
				{
					if (angular.element(this).hasClass('select2-container'))
						return;

					// This should be automatic but it is invisible still at this point so not processed yet.
					angular.element(this).val(ctrl.task.priority);

					ctrl.colourSelect(angular.element(this));
				});
			};

			ctrl.colourSelect = function(target)
			{
				target.select2({
					templateResult: function (data)
					{
						if (!data.id) { return data.text; }
						return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
					},
					templateSelection: function formatPrioritySelection (data)
					{
						if (!data.id) { return data.text; }
						return '<span class="task-priority-option task-priority-' + data.id + '" style="display:inline-block; margin-right:0;">&nbsp</span> ' + data.text;
					},
					escapeMarkup: function(markup)
					{
						return markup;
					},
					minimumResultsForSearch: Infinity, // Hide search box
					width: "100%"
				});
			};
		}

		function link(scope, element, attrs, controller)
		{
		    controller.$modal = angular.element('#task-edit-modal');
		    service.registerModal(controller.$modal);

		    controller.$modal.on('taskEdit', function(event, task){
		        controller.task = angular.copy(task);

                $timeout(function() {
                    controller.startTaskEdit();
                }, 0);
            });

            controller.$modal.on('taskEditFinished', function(event){
                controller.endTaskEdit(event);
            });

		    // Handle the modal closing for any reason
            controller.$modal.on('hidden.bs.modal', function(){
                controller.htmlControl.onClose();

                controller.visible = false;
            });
		}
	}
}());


/***/ }),

/***/ "./js/task/taskEdit.service.js":
/***/ (function(module, exports) {

(function()
{
    var moduleName = 'claromentis.project.tasks_util';
    var module;

    module = angular.module(moduleName);

    module.service('task_edit_service', TaskEditService);

    function TaskEditService()
    {
        var service = this;

        service.$currentElement = null
        service.$modalElement = null

        service.startEdit = function(task, $taskElement)
        {
            if (service.$currentElement !== null)
            {
                service.$currentElement.trigger('cancelEdit');
                service.$currentElement = null;
            }

            service.$currentElement = $taskElement;
            service.$modalElement.trigger('taskEdit', task);
        };

        service.taskUpdated = function(task, callback)
        {
            var updateFunc = 'update';
            if (task.id === 0)
                updateFunc = 'add';

            task[updateFunc](function(data)
            {
                service.$modalElement.trigger('taskEditFinished', task);

                if (service.$currentElement !== null)
                {
                    service.$currentElement.trigger('editComplete', task);
                    service.$currentElement = null;
                }

                cla.showMessage(lmsg('projects.history.type.task_updated'), '', false);

                callback();
            }, function(data)
            {
                cla.showMessage(data.error, '', true);
            });

            return task;
        };

        service.taskDeleted = function(task)
        {
            if (service.$currentElement !== null)
            {
                service.$currentElement.trigger('taskDeleted', task);
                service.$currentElement = null;
            }
        };

        service.registerModal = function($modalElement)
        {
          service.$modalElement = $modalElement;
        };
    }
}());


/***/ }),

/***/ "./node_modules/angular-utils-pagination/dirPagination.js":
/***/ (function(module, exports) {

/**
 * dirPagination - AngularJS module for paginating (almost) anything.
 *
 *
 * Credits
 * =======
 *
 * Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ
 * for the idea on how to dynamically invoke the ng-repeat directive.
 *
 * I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project:
 * https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js
 *
 * Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk>
 */

(function() {

    /**
     * Config
     */
    var moduleName = 'angularUtils.directives.dirPagination';
    var DEFAULT_ID = '__default';

    /**
     * Module
     */
    angular.module(moduleName, [])
        .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
        .directive('dirPaginateNoCompile', noCompileDirective)
        .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
        .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
        .service('paginationService', paginationService)
        .provider('paginationTemplate', paginationTemplateProvider)
        .run(['$templateCache',dirPaginationControlsTemplateInstaller]);

    function dirPaginateDirective($compile, $parse, paginationService) {

        return  {
            terminal: true,
            multiElement: true,
            priority: 100,
            compile: dirPaginationCompileFn
        };

        function dirPaginationCompileFn(tElement, tAttrs){

            var expression = tAttrs.dirPaginate;
            // regex taken directly from https://github.com/angular/angular.js/blob/v1.4.x/src/ng/directive/ngRepeat.js#L339
            var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

            var filterPattern = /\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;
            if (match[2].match(filterPattern) === null) {
                throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
            }
            var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
            var collectionGetter = $parse(itemsPerPageFilterRemoved);

            addNoCompileAttributes(tElement);

            // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
            // dir-pagination-controls directives that may be looking for this ID.
            var rawId = tAttrs.paginationId || DEFAULT_ID;
            paginationService.registerInstance(rawId);

            return function dirPaginationLinkFn(scope, element, attrs){

                // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
                // potentially register a new ID if it evaluates to a different value than the rawId.
                var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
                
                // (TODO: this seems sound, but I'm reverting as many bug reports followed it's introduction in 0.11.0.
                // Needs more investigation.)
                // In case rawId != paginationId we deregister using rawId for the sake of general cleanliness
                // before registering using paginationId
                // paginationService.deregisterInstance(rawId);
                paginationService.registerInstance(paginationId);

                var repeatExpression = getRepeatExpression(expression, paginationId);
                addNgRepeatToElement(element, attrs, repeatExpression);

                removeTemporaryAttributes(element);
                var compiled =  $compile(element);

                var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
                paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);

                if (typeof attrs.totalItems !== 'undefined') {
                    paginationService.setAsyncModeTrue(paginationId);
                    scope.$watch(function() {
                        return $parse(attrs.totalItems)(scope);
                    }, function (result) {
                        if (0 <= result) {
                            paginationService.setCollectionLength(paginationId, result);
                        }
                    });
                } else {
                    paginationService.setAsyncModeFalse(paginationId);
                    scope.$watchCollection(function() {
                        return collectionGetter(scope);
                    }, function(collection) {
                        if (collection) {
                            var collectionLength = (collection instanceof Array) ? collection.length : Object.keys(collection).length;
                            paginationService.setCollectionLength(paginationId, collectionLength);
                        }
                    });
                }

                // Delegate to the link function returned by the new compilation of the ng-repeat
                compiled(scope);
                 
                // (TODO: Reverting this due to many bug reports in v 0.11.0. Needs investigation as the
                // principle is sound)
                // When the scope is destroyed, we make sure to remove the reference to it in paginationService
                // so that it can be properly garbage collected
                // scope.$on('$destroy', function destroyDirPagination() {
                //     paginationService.deregisterInstance(paginationId);
                // });
            };
        }

        /**
         * If a pagination id has been specified, we need to check that it is present as the second argument passed to
         * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
         *
         * @param expression
         * @param paginationId
         * @returns {*}
         */
        function getRepeatExpression(expression, paginationId) {
            var repeatExpression,
                idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);

            if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
                repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/, "$1 : '" + paginationId + "'");
            } else {
                repeatExpression = expression;
            }

            return repeatExpression;
        }

        /**
         * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
         * appropriate multi-element ng-repeat to the first and last element in the range.
         * @param element
         * @param attrs
         * @param repeatExpression
         */
        function addNgRepeatToElement(element, attrs, repeatExpression) {
            if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
                // using multiElement mode (dir-paginate-start, dir-paginate-end)
                attrs.$set('ngRepeatStart', repeatExpression);
                element.eq(element.length - 1).attr('ng-repeat-end', true);
            } else {
                attrs.$set('ngRepeat', repeatExpression);
            }
        }

        /**
         * Adds the dir-paginate-no-compile directive to each element in the tElement range.
         * @param tElement
         */
        function addNoCompileAttributes(tElement) {
            angular.forEach(tElement, function(el) {
                if (el.nodeType === 1) {
                    angular.element(el).attr('dir-paginate-no-compile', true);
                }
            });
        }

        /**
         * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
         * @param element
         */
        function removeTemporaryAttributes(element) {
            angular.forEach(element, function(el) {
                if (el.nodeType === 1) {
                    angular.element(el).removeAttr('dir-paginate-no-compile');
                }
            });
            element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
            element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
        }

        /**
         * Creates a getter function for the current-page attribute, using the expression provided or a default value if
         * no current-page expression was specified.
         *
         * @param scope
         * @param attrs
         * @param paginationId
         * @returns {*}
         */
        function makeCurrentPageGetterFn(scope, attrs, paginationId) {
            var currentPageGetter;
            if (attrs.currentPage) {
                currentPageGetter = $parse(attrs.currentPage);
            } else {
                // If the current-page attribute was not set, we'll make our own.
                // Replace any non-alphanumeric characters which might confuse
                // the $parse service and give unexpected results.
                // See https://github.com/michaelbromley/angularUtils/issues/233
                var defaultCurrentPage = (paginationId + '__currentPage').replace(/\W/g, '_');
                scope[defaultCurrentPage] = 1;
                currentPageGetter = $parse(defaultCurrentPage);
            }
            return currentPageGetter;
        }
    }

    /**
     * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
     * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
     * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
     */
    function noCompileDirective() {
        return {
            priority: 5000,
            terminal: true
        };
    }

    function dirPaginationControlsTemplateInstaller($templateCache) {
        $templateCache.put('angularUtils.directives.dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length || !autoHide"><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(1)">&laquo;</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)">&lsaquo;</a></li><li ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' || ( ! autoHide && pages.length === 1 ) }"><a href="" ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.current + 1)">&rsaquo;</a></li><li ng-if="boundaryLinks"  ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">&raquo;</a></li></ul>');
    }

    function dirPaginationControlsDirective(paginationService, paginationTemplate) {

        var numberRegex = /^\d+$/;

        var DDO = {
            restrict: 'AE',
            scope: {
                maxSize: '=?',
                onPageChange: '&?',
                paginationId: '=?',
                autoHide: '=?'
            },
            link: dirPaginationControlsLinkFn
        };

        // We need to check the paginationTemplate service to see whether a template path or
        // string has been specified, and add the `template` or `templateUrl` property to
        // the DDO as appropriate. The order of priority to decide which template to use is
        // (highest priority first):
        // 1. paginationTemplate.getString()
        // 2. attrs.templateUrl
        // 3. paginationTemplate.getPath()
        var templateString = paginationTemplate.getString();
        if (templateString !== undefined) {
            DDO.template = templateString;
        } else {
            DDO.templateUrl = function(elem, attrs) {
                return attrs.templateUrl || paginationTemplate.getPath();
            };
        }
        return DDO;

        function dirPaginationControlsLinkFn(scope, element, attrs) {

            // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
            // not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is
            // no corresponding dir-paginate directive and wrongly throwing an exception.
            var rawId = attrs.paginationId ||  DEFAULT_ID;
            var paginationId = scope.paginationId || attrs.paginationId ||  DEFAULT_ID;

            if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
                var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
                if (window.console) {
                    console.warn('Pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive, which was not found at link time.');
                }
            }

            if (!scope.maxSize) { scope.maxSize = 9; }
            scope.autoHide = scope.autoHide === undefined ? true : scope.autoHide;
            scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
            scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;

            var paginationRange = Math.max(scope.maxSize, 5);
            scope.pages = [];
            scope.pagination = {
                last: 1,
                current: 1
            };
            scope.range = {
                lower: 1,
                upper: 1,
                total: 1
            };

            scope.$watch('maxSize', function(val) {
                if (val) {
                    paginationRange = Math.max(scope.maxSize, 5);
                    generatePagination();
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
                }
            }, function(length) {
                if (0 < length) {
                    generatePagination();
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return (paginationService.getItemsPerPage(paginationId));
                }
            }, function(current, previous) {
                if (current != previous && typeof previous !== 'undefined') {
                    goToPage(scope.pagination.current);
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return paginationService.getCurrentPage(paginationId);
                }
            }, function(currentPage, previousPage) {
                if (currentPage != previousPage) {
                    goToPage(currentPage);
                }
            });

            scope.setCurrent = function(num) {
                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
                    num = parseInt(num, 10);
                    paginationService.setCurrentPage(paginationId, num);
                }
            };

            /**
             * Custom "track by" function which allows for duplicate "..." entries on long lists,
             * yet fixes the problem of wrongly-highlighted links which happens when using
             * "track by $index" - see https://github.com/michaelbromley/angularUtils/issues/153
             * @param id
             * @param index
             * @returns {string}
             */
            scope.tracker = function(id, index) {
                return id + '_' + index;
            };

            function goToPage(num) {
                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
                    var oldPageNumber = scope.pagination.current;

                    scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
                    scope.pagination.current = num;
                    updateRangeValues();

                    // if a callback has been set, then call it with the page number as the first argument
                    // and the previous page number as a second argument
                    if (scope.onPageChange) {
                        scope.onPageChange({
                            newPageNumber : num,
                            oldPageNumber : oldPageNumber
                        });
                    }
                }
            }

            function generatePagination() {
                if (paginationService.isRegistered(paginationId)) {
                    var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
                    scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
                    scope.pagination.current = page;
                    scope.pagination.last = scope.pages[scope.pages.length - 1];
                    if (scope.pagination.last < scope.pagination.current) {
                        scope.setCurrent(scope.pagination.last);
                    } else {
                        updateRangeValues();
                    }
                }
            }

            /**
             * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
             * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
             */
            function updateRangeValues() {
                if (paginationService.isRegistered(paginationId)) {
                    var currentPage = paginationService.getCurrentPage(paginationId),
                        itemsPerPage = paginationService.getItemsPerPage(paginationId),
                        totalItems = paginationService.getCollectionLength(paginationId);

                    scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
                    scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
                    scope.range.total = totalItems;
                }
            }
            function isValidPageNumber(num) {
                return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
            }
        }

        /**
         * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
         * links used in pagination
         *
         * @param currentPage
         * @param rowsPerPage
         * @param paginationRange
         * @param collectionLength
         * @returns {Array}
         */
        function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
            var pages = [];
            var totalPages = Math.ceil(collectionLength / rowsPerPage);
            var halfWay = Math.ceil(paginationRange / 2);
            var position;

            if (currentPage <= halfWay) {
                position = 'start';
            } else if (totalPages - halfWay < currentPage) {
                position = 'end';
            } else {
                position = 'middle';
            }

            var ellipsesNeeded = paginationRange < totalPages;
            var i = 1;
            while (i <= totalPages && i <= paginationRange) {
                var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);

                var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
                var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
                if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
                    pages.push('...');
                } else {
                    pages.push(pageNumber);
                }
                i ++;
            }
            return pages;
        }

        /**
         * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
         *
         * @param i
         * @param currentPage
         * @param paginationRange
         * @param totalPages
         * @returns {*}
         */
        function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
            var halfWay = Math.ceil(paginationRange/2);
            if (i === paginationRange) {
                return totalPages;
            } else if (i === 1) {
                return i;
            } else if (paginationRange < totalPages) {
                if (totalPages - halfWay < currentPage) {
                    return totalPages - paginationRange + i;
                } else if (halfWay < currentPage) {
                    return currentPage - halfWay + i;
                } else {
                    return i;
                }
            } else {
                return i;
            }
        }
    }

    /**
     * This filter slices the collection into pages based on the current page number and number of items per page.
     * @param paginationService
     * @returns {Function}
     */
    function itemsPerPageFilter(paginationService) {

        return function(collection, itemsPerPage, paginationId) {
            if (typeof (paginationId) === 'undefined') {
                paginationId = DEFAULT_ID;
            }
            if (!paginationService.isRegistered(paginationId)) {
                throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
            }
            var end;
            var start;
            if (angular.isObject(collection)) {
                itemsPerPage = parseInt(itemsPerPage) || 9999999999;
                if (paginationService.isAsyncMode(paginationId)) {
                    start = 0;
                } else {
                    start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
                }
                end = start + itemsPerPage;
                paginationService.setItemsPerPage(paginationId, itemsPerPage);

                if (collection instanceof Array) {
                    // the array just needs to be sliced
                    return collection.slice(start, end);
                } else {
                    // in the case of an object, we need to get an array of keys, slice that, then map back to
                    // the original object.
                    var slicedObject = {};
                    angular.forEach(keys(collection).slice(start, end), function(key) {
                        slicedObject[key] = collection[key];
                    });
                    return slicedObject;
                }
            } else {
                return collection;
            }
        };
    }

    /**
     * Shim for the Object.keys() method which does not exist in IE < 9
     * @param obj
     * @returns {Array}
     */
    function keys(obj) {
        if (!Object.keys) {
            var objKeys = [];
            for (var i in obj) {
                if (obj.hasOwnProperty(i)) {
                    objKeys.push(i);
                }
            }
            return objKeys;
        } else {
            return Object.keys(obj);
        }
    }

    /**
     * This service allows the various parts of the module to communicate and stay in sync.
     */
    function paginationService() {

        var instances = {};
        var lastRegisteredInstance;

        this.registerInstance = function(instanceId) {
            if (typeof instances[instanceId] === 'undefined') {
                instances[instanceId] = {
                    asyncMode: false
                };
                lastRegisteredInstance = instanceId;
            }
        };

        this.deregisterInstance = function(instanceId) {
            delete instances[instanceId];
        };
        
        this.isRegistered = function(instanceId) {
            return (typeof instances[instanceId] !== 'undefined');
        };

        this.getLastInstanceId = function() {
            return lastRegisteredInstance;
        };

        this.setCurrentPageParser = function(instanceId, val, scope) {
            instances[instanceId].currentPageParser = val;
            instances[instanceId].context = scope;
        };
        this.setCurrentPage = function(instanceId, val) {
            instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
        };
        this.getCurrentPage = function(instanceId) {
            var parser = instances[instanceId].currentPageParser;
            return parser ? parser(instances[instanceId].context) : 1;
        };

        this.setItemsPerPage = function(instanceId, val) {
            instances[instanceId].itemsPerPage = val;
        };
        this.getItemsPerPage = function(instanceId) {
            return instances[instanceId].itemsPerPage;
        };

        this.setCollectionLength = function(instanceId, val) {
            instances[instanceId].collectionLength = val;
        };
        this.getCollectionLength = function(instanceId) {
            return instances[instanceId].collectionLength;
        };

        this.setAsyncModeTrue = function(instanceId) {
            instances[instanceId].asyncMode = true;
        };

        this.setAsyncModeFalse = function(instanceId) {
            instances[instanceId].asyncMode = false;
        };

        this.isAsyncMode = function(instanceId) {
            return instances[instanceId].asyncMode;
        };
    }

    /**
     * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
     */
    function paginationTemplateProvider() {

        var templatePath = 'angularUtils.directives.dirPagination.template';
        var templateString;

        /**
         * Set a templateUrl to be used by all instances of <dir-pagination-controls>
         * @param {String} path
         */
        this.setPath = function(path) {
            templatePath = path;
        };

        /**
         * Set a string of HTML to be used as a template by all instances
         * of <dir-pagination-controls>. If both a path *and* a string have been set,
         * the string takes precedence.
         * @param {String} str
         */
        this.setString = function(str) {
            templateString = str;
        };

        this.$get = function() {
            return {
                getPath: function() {
                    return templatePath;
                },
                getString: function() {
                    return templateString;
                }
            };
        };
    }
})();


/***/ }),

/***/ 0:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_0__;

/***/ }),

/***/ 1:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_1__;

/***/ }),

/***/ 10:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_10__;

/***/ }),

/***/ 11:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_11__;

/***/ }),

/***/ 12:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_12__;

/***/ }),

/***/ 15:
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__("./assets/css/style.scss");
__webpack_require__("./assets/css/project_details.scss");
__webpack_require__("./assets/css/ng-sortable.scss");
module.exports = __webpack_require__("./js/projects.detail.js");


/***/ }),

/***/ 2:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_2__;

/***/ }),

/***/ 3:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_3__;

/***/ }),

/***/ 4:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_4__;

/***/ }),

/***/ 5:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_5__;

/***/ }),

/***/ 6:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_6__;

/***/ }),

/***/ 7:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_7__;

/***/ }),

/***/ 9:
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_9__;

/***/ })

/******/ })});;