diff options
173 files changed, 1526 insertions, 1654 deletions
@@ -1,3 +1,13 @@ +Version 1.0.37 (unreleased) +--------------------------- + +Improvements: + +* Larger task form +* Rewrite dialog and confirmation boxes (inline popup) +* Remove TaskGanttCreationController +* Add helpers to open modal boxes + Version 1.0.36 (Dec 30, 2016) ----------------------------- diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php index 228d681c..ae39fdf3 100644 --- a/app/Controller/ProjectEditController.php +++ b/app/Controller/ProjectEditController.php @@ -11,51 +11,23 @@ namespace Kanboard\Controller; class ProjectEditController extends BaseController { /** - * General edition (most common operations) + * Edit project * * @access public * @param array $values * @param array $errors */ - public function edit(array $values = array(), array $errors = array()) + public function show(array $values = array(), array $errors = array()) { - $this->renderView('project_edit/general', $values, $errors); - } - - /** - * Change start and end dates - * - * @access public - * @param array $values - * @param array $errors - */ - public function dates(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/dates', $values, $errors); - } - - /** - * Change project description - * - * @access public - * @param array $values - * @param array $errors - */ - public function description(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/description', $values, $errors); - } + $project = $this->getProject(); - /** - * Change task priority - * - * @access public - * @param array $values - * @param array $errors - */ - public function priority(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/task_priority', $values, $errors); + $this->response->html($this->helper->layout->project('project_edit/show', array( + 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true), + 'values' => empty($values) ? $project : $values, + 'errors' => $errors, + 'project' => $project, + 'title' => t('Edit project') + ))); } /** @@ -67,67 +39,42 @@ class ProjectEditController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); - $redirect = $this->request->getStringParam('redirect', 'edit'); - $values = $this->prepareValues($redirect, $project, $values); + $values = $this->prepareValues($project, $values); list($valid, $errors) = $this->projectValidator->validateModification($values); if ($valid) { if ($this->projectModel->update($values)) { $this->flash->success(t('Project updated successfully.')); - return $this->response->redirect($this->helper->url->to('ProjectEditController', $redirect, array('project_id' => $project['id'])), true); + return $this->response->redirect($this->helper->url->to('ProjectEditController', 'show', array('project_id' => $project['id'])), true); } else { $this->flash->failure(t('Unable to update this project.')); } } - return $this->$redirect($values, $errors); + return $this->show($values, $errors); } /** * Prepare form values * * @access private - * @param string $redirect * @param array $project * @param array $values * @return array */ - private function prepareValues($redirect, array $project, array $values) + private function prepareValues(array $project, array $values) { - if ($redirect === 'edit') { - if (isset($values['is_private'])) { - if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { - unset($values['is_private']); - } - } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { - if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { - $values += array('is_private' => 0); - } + if (isset($values['is_private'])) { + if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { + unset($values['is_private']); + } + } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { + if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { + $values += array('is_private' => 0); } } return $values; } - - /** - * Common method to render different views - * - * @access private - * @param string $template - * @param array $values - * @param array $errors - */ - private function renderView($template, array $values, array $errors) - { - $project = $this->getProject(); - - $this->response->html($this->helper->layout->project($template, array( - 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true), - 'values' => empty($values) ? $project : $values, - 'errors' => $errors, - 'project' => $project, - 'title' => t('Edit project') - ))); - } } diff --git a/app/Controller/TaskGanttCreationController.php b/app/Controller/TaskGanttCreationController.php deleted file mode 100644 index c5046f60..00000000 --- a/app/Controller/TaskGanttCreationController.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -/** - * Class TaskGanttCreationController - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class TaskGanttCreationController extends BaseController -{ - /** - * Simplified form to create a new task - * - * @access public - * @param array $values - * @param array $errors - * @throws \Kanboard\Core\Controller\PageNotFoundException - */ - public function show(array $values = array(), array $errors = array()) - { - $project = $this->getProject(); - - $values = $values + array( - 'project_id' => $project['id'], - 'column_id' => $this->columnModel->getFirstColumnId($project['id']), - 'position' => 1 - ); - - $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); - $values = $this->hook->merge('controller:gantt:task:form:default', $values, array('default_values' => $values)); - - $this->response->html($this->template->render('task_gantt_creation/show', array( - 'project' => $project, - 'errors' => $errors, - 'values' => $values, - 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, true), - 'categories_list' => $this->categoryModel->getList($project['id']), - 'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true), - ))); - } - - /** - * Validate and save a new task - * - * @access public - */ - public function save() - { - $project = $this->getProject(); - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateCreation($values); - - if ($valid && $this->taskCreationModel->create($values)) { - $this->flash->success(t('Task created successfully.')); - $this->response->redirect($this->helper->url->to('TaskGanttController', 'show', array('project_id' => $project['id']))); - } else { - $this->flash->failure(t('Unable to create your task.')); - $this->show($values, $errors); - } - } -} diff --git a/app/Controller/TaskMovePositionController.php b/app/Controller/TaskMovePositionController.php index caa074c9..39687b79 100644 --- a/app/Controller/TaskMovePositionController.php +++ b/app/Controller/TaskMovePositionController.php @@ -38,7 +38,7 @@ class TaskMovePositionController extends BaseController throw new AccessForbiddenException(e('You are not allowed to move this task.')); } - $result = $this->taskPositionModel->movePosition( + $this->taskPositionModel->movePosition( $task['project_id'], $task['id'], $values['column_id'], @@ -46,6 +46,6 @@ class TaskMovePositionController extends BaseController $values['swimlane_id'] ); - $this->response->json(array('result' => $result)); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); } } diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 9660c348..ab7c3b7b 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -20,6 +20,7 @@ use Pimple\Container; * @property \Kanboard\Helper\FormHelper $form * @property \Kanboard\Helper\HookHelper $hook * @property \Kanboard\Helper\ICalHelper $ical + * @property \Kanboard\Helper\ModalHelper $modal * @property \Kanboard\Helper\ModelHelper $model * @property \Kanboard\Helper\SubtaskHelper $subtask * @property \Kanboard\Helper\TaskHelper $task diff --git a/app/Helper/ModalHelper.php b/app/Helper/ModalHelper.php new file mode 100644 index 00000000..e6037a8c --- /dev/null +++ b/app/Helper/ModalHelper.php @@ -0,0 +1,73 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Class ModalHelper + * + * @package Kanboard\Helper + * @author Frederic Guillot + */ +class ModalHelper extends Base +{ + public function submitButtons(array $params = array()) + { + return $this->helper->app->component('submit-buttons', array( + 'submitLabel' => isset($params['submitLabel']) ? $params['submitLabel'] : t('Save'), + 'orLabel' => t('or'), + 'cancelLabel' => t('cancel'), + 'color' => isset($params['color']) ? $params['color'] : 'blue', + 'tabindex' => isset($params['tabindex']) ? $params['tabindex'] : null, + 'disabled' => isset($params['disabled']) ? true : false, + )); + } + + public function confirmButtons($controller, $action, array $params = array(), $submitLabel = '', $tabindex = null) + { + return $this->helper->app->component('confirm-buttons', array( + 'url' => $this->helper->url->href($controller, $action, $params, true), + 'submitLabel' => $submitLabel ?: t('Yes'), + 'orLabel' => t('or'), + 'cancelLabel' => t('cancel'), + 'tabindex' => $tabindex, + )); + } + + public function largeIcon($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-large" aria-hidden="true"></i>'; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-large', $label); + } + + public function large($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-large" aria-hidden="true"></i> '.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-large'); + } + + public function medium($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" aria-hidden="true"></i> '.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium'); + } + + public function small($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-small" aria-hidden="true"></i> '.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-small'); + } + + public function mediumButton($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" aria-hidden="true"></i> '.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium btn'); + } + + public function confirm($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-confirm" aria-hidden="true"></i> '.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-confirm'); + } +} diff --git a/app/Helper/TaskHelper.php b/app/Helper/TaskHelper.php index 92a9228c..a13ad18b 100644 --- a/app/Helper/TaskHelper.php +++ b/app/Helper/TaskHelper.php @@ -42,16 +42,23 @@ class TaskHelper extends Base public function selectTitle(array $values, array $errors) { - $html = $this->helper->form->label(t('Title'), 'title'); - $html .= $this->helper->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"'), 'form-input-large'); - return $html; + return $this->helper->form->text( + 'title', + $values, + $errors, + array( + 'autofocus', + 'required', + 'maxlength="200"', + 'tabindex="1"', + 'placeholder="'.t('Title').'"' + ) + ); } public function selectDescription(array $values, array $errors) { - $html = $this->helper->form->label(t('Description'), 'description'); - $html .= $this->helper->form->textEditor('description', $values, $errors, array('tabindex' => 2)); - return $html; + return $this->helper->form->textEditor('description', $values, $errors, array('tabindex' => 2)); } public function selectTags(array $project, array $tags = array()) @@ -152,7 +159,7 @@ class TaskHelper extends Base public function selectScore(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="8"'), $attributes); + $attributes = array_merge(array('tabindex="13"'), $attributes); $html = $this->helper->form->label(t('Complexity'), 'score'); $html .= $this->helper->form->number('score', $values, $errors, $attributes); @@ -162,7 +169,7 @@ class TaskHelper extends Base public function selectReference(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="9"'), $attributes); + $attributes = array_merge(array('tabindex="14"'), $attributes); $html = $this->helper->form->label(t('Reference'), 'reference'); $html .= $this->helper->form->text('reference', $values, $errors, $attributes, 'form-input-small'); @@ -172,7 +179,7 @@ class TaskHelper extends Base public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="10"'), $attributes); + $attributes = array_merge(array('tabindex="11"'), $attributes); $html = $this->helper->form->label(t('Original estimate'), 'time_estimated'); $html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes); @@ -183,7 +190,7 @@ class TaskHelper extends Base public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="11"'), $attributes); + $attributes = array_merge(array('tabindex="12"'), $attributes); $html = $this->helper->form->label(t('Time spent'), 'time_spent'); $html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes); @@ -194,13 +201,13 @@ class TaskHelper extends Base public function selectStartDate(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="12"'), $attributes); + $attributes = array_merge(array('tabindex="10"'), $attributes); return $this->helper->form->datetime(t('Start Date'), 'date_started', $values, $errors, $attributes); } public function selectDueDate(array $values, array $errors = array(), array $attributes = array()) { - $attributes = array_merge(array('tabindex="13"'), $attributes); + $attributes = array_merge(array('tabindex="9"'), $attributes); return $this->helper->form->date(t('Due Date'), 'date_due', $values, $errors, $attributes); } diff --git a/app/Helper/UrlHelper.php b/app/Helper/UrlHelper.php index 93177ed5..bfcf07e6 100644 --- a/app/Helper/UrlHelper.php +++ b/app/Helper/UrlHelper.php @@ -42,9 +42,9 @@ class UrlHelper extends Base */ public function button($icon, $label, $controller, $action, array $params = array(), $class = '') { - $icon = '<i class="fa '.$icon.' fa-fw"></i> '; + $html = '<i class="fa fa-'.$icon.' fa-fw"></i> '.$label; $class = 'btn '.$class; - return $this->link($icon.$label, $controller, $action, $params, false, $class); + return $this->link($html, $controller, $action, $params, false, $class); } /** diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php index 80882456..c2978467 100644 --- a/app/ServiceProvider/AuthenticationProvider.php +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -88,7 +88,6 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('ExportController', '*', Role::PROJECT_MANAGER); $acl->add('TaskFileController', array('screenshot', 'create', 'save', 'remove', 'confirm'), Role::PROJECT_MEMBER); $acl->add('TaskGanttController', '*', Role::PROJECT_MANAGER); - $acl->add('TaskGanttCreationController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectViewController', array('share', 'updateSharing', 'integrations', 'updateIntegrations', 'notifications', 'updateNotifications', 'duplicate', 'doDuplication'), Role::PROJECT_MANAGER); $acl->add('ProjectPermissionController', '*', Role::PROJECT_MANAGER); $acl->add('ProjectEditController', '*', Role::PROJECT_MANAGER); diff --git a/app/ServiceProvider/HelperProvider.php b/app/ServiceProvider/HelperProvider.php index f4c0db22..dcaf81c6 100644 --- a/app/ServiceProvider/HelperProvider.php +++ b/app/ServiceProvider/HelperProvider.php @@ -39,6 +39,7 @@ class HelperProvider implements ServiceProviderInterface $container['helper']->register('projectHeader', '\Kanboard\Helper\ProjectHeaderHelper'); $container['helper']->register('projectActivity', '\Kanboard\Helper\ProjectActivityHelper'); $container['helper']->register('mail', '\Kanboard\Helper\MailHelper'); + $container['helper']->register('modal', '\Kanboard\Helper\ModalHelper'); $container['template'] = new Template($container['helper']); diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php index 52687647..08759b22 100644 --- a/app/ServiceProvider/RouteProvider.php +++ b/app/ServiceProvider/RouteProvider.php @@ -65,10 +65,7 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('project/:project_id/overview', 'ProjectOverviewController', 'show'); // ProjectEdit routes - $container['route']->addRoute('project/:project_id/edit', 'ProjectEditController', 'edit'); - $container['route']->addRoute('project/:project_id/edit/dates', 'ProjectEditController', 'dates'); - $container['route']->addRoute('project/:project_id/edit/description', 'ProjectEditController', 'description'); - $container['route']->addRoute('project/:project_id/edit/priority', 'ProjectEditController', 'priority'); + $container['route']->addRoute('project/:project_id/edit', 'ProjectEditController', 'show'); // ProjectUser routes $container['route']->addRoute('projects/managers/:user_id', 'ProjectUserOverviewController', 'managers'); diff --git a/app/Template/action/index.php b/app/Template/action/index.php index 7768a0b6..90db0450 100644 --- a/app/Template/action/index.php +++ b/app/Template/action/index.php @@ -2,12 +2,10 @@ <h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2> <ul> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a new action'), 'ActionCreationController', 'create', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new action'), 'ActionCreationController', 'create', array('project_id' => $project['id'])) ?> </li> <li> - <i class="fa fa-copy fa-fw"></i> - <?= $this->url->link(t('Import from another project'), 'ProjectActionDuplicationController', 'show', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('copy', t('Import from another project'), 'ProjectActionDuplicationController', 'show', array('project_id' => $project['id'])) ?> </li> </ul> </div> @@ -17,8 +15,8 @@ <?php else: ?> <table class="table-scrolling"> <tr> - <th><?= t('Automatic actions') ?></th> - <th><?= t('Action parameters') ?></th> + <th class="column-60"><?= t('Automatic actions') ?></th> + <th class="column-25"><?= t('Action parameters') ?></th> <th><?= t('Action') ?></th> </tr> @@ -65,7 +63,7 @@ </ul> </td> <td> - <?= $this->url->link(t('Remove'), 'ActionController', 'confirm', array('project_id' => $project['id'], 'action_id' => $action['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ActionController', 'confirm', array('project_id' => $project['id'], 'action_id' => $action['id'])) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/action/remove.php b/app/Template/action/remove.php index 384bec7a..e3cdb206 100644 --- a/app/Template/action/remove.php +++ b/app/Template/action/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this action: "%s"?', $this->text->in($action['event_name'], $available_events).'/'.$this->text->in($action['action_name'], $available_actions)) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ActionController', 'remove', array('project_id' => $project['id'], 'action_id' => $action['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ActionController', + 'remove', + array('project_id' => $project['id'], 'action_id' => $action['id']) + ) ?> </div> diff --git a/app/Template/action_creation/create.php b/app/Template/action_creation/create.php index c0d2880e..04aa761c 100644 --- a/app/Template/action_creation/create.php +++ b/app/Template/action_creation/create.php @@ -1,16 +1,12 @@ <div class="page-header"> <h2><?= t('Add an action') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>"> +<form method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Action'), 'action_name') ?> <?= $this->form->select('action_name', $available_actions, $values) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Next step') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ActionController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons(t('Next step')) ?> </form> diff --git a/app/Template/action_creation/event.php b/app/Template/action_creation/event.php index cdf00310..58996ad8 100644 --- a/app/Template/action_creation/event.php +++ b/app/Template/action_creation/event.php @@ -2,8 +2,7 @@ <h2><?= t('Choose an event') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>"> - +<form method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> @@ -19,9 +18,5 @@ <?= t('When the selected event occurs execute the corresponding action.') ?> </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Next step') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons(t('Next step')) ?> </form> diff --git a/app/Template/action_creation/params.php b/app/Template/action_creation/params.php index c9608f21..0cc98f50 100644 --- a/app/Template/action_creation/params.php +++ b/app/Template/action_creation/params.php @@ -2,8 +2,7 @@ <h2><?= t('Define action parameters') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - +<form method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> @@ -50,9 +49,5 @@ <?php endif ?> <?php endforeach ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php index e2976f27..c334cda4 100644 --- a/app/Template/board/table_column.php +++ b/app/Template/board/table_column.php @@ -14,7 +14,7 @@ <div class="board-column-expanded"> <?php if (! $not_editable && $this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?> <div class="board-add-icon"> - <?= $this->url->link('+', 'TaskCreationController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover', t('Add a new task')) ?> + <?= $this->modal->largeIcon('plus', t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?> </div> <?php endif ?> @@ -37,15 +37,13 @@ </li> <?php if ($this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?> <li> - <i class="fa fa-align-justify fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Create tasks in bulk'), 'TaskBulkController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?> + <?= $this->modal->medium('align-justify', t('Create tasks in bulk'), 'TaskBulkController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?> </li> <?php endif ?> <?php if ($column['nb_tasks'] > 0 && $this->projectRole->canChangeTaskStatusInColumn($column['project_id'], $column['id'])): ?> <li> - <i class="fa fa-close fa-fw"></i> - <?= $this->url->link(t('Close all tasks of this column'), 'BoardPopoverController', 'confirmCloseColumnTasks', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?> + <?= $this->modal->confirm('close', t('Close all tasks of this column'), 'BoardPopoverController', 'confirmCloseColumnTasks', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?> </li> <?php endif ?> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php index 83a582c6..15f1f713 100644 --- a/app/Template/board/task_footer.php +++ b/app/Template/board/task_footer.php @@ -10,7 +10,7 @@ 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, - 'popover' . (! empty($task['category_description']) ? ' tooltip' : ''), + 'js-modal-medium' . (! empty($task['category_description']) ? ' tooltip' : ''), ! empty($task['category_description']) ? $this->text->markdownAttribute($task['category_description']) : t('Change category') ) ?> <?php endif ?> diff --git a/app/Template/board/tooltip_files.php b/app/Template/board/tooltip_files.php index 4e704dac..a372b1a0 100644 --- a/app/Template/board/tooltip_files.php +++ b/app/Template/board/tooltip_files.php @@ -11,7 +11,7 @@ <td> <i class="fa fa-download fa-fw"></i><?= $this->url->link(t('download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?> <?php if ($file['is_image'] == 1): ?> - <i class="fa fa-eye"></i> <?= $this->url->link(t('open file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->large('eye', t('open file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?> <?php endif ?> </td> </tr> diff --git a/app/Template/board_popover/close_all_tasks_column.php b/app/Template/board_popover/close_all_tasks_column.php index 57f703e3..54ea3220 100644 --- a/app/Template/board_popover/close_all_tasks_column.php +++ b/app/Template/board_popover/close_all_tasks_column.php @@ -1,18 +1,12 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('Do you really want to close all tasks of this column?') ?></h2> - </div> - <form method="post" action="<?= $this->url->href('BoardPopoverController', 'closeColumnTasks', array('project_id' => $project['id'])) ?>"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('column_id', $values) ?> - <?= $this->form->hidden('swimlane_id', $values) ?> +<div class="page-header"> + <h2><?= t('Do you really want to close all tasks of this column?') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('BoardPopoverController', 'closeColumnTasks', array('project_id' => $project['id'])) ?>"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('column_id', $values) ?> + <?= $this->form->hidden('swimlane_id', $values) ?> - <p class="alert"><?= t('%d task(s) in the column "%s" and the swimlane "%s" will be closed.', $nb_tasks, $column, $swimlane) ?></p> + <p class="alert"><?= t('%d task(s) in the column "%s" and the swimlane "%s" will be closed.', $nb_tasks, $column, $swimlane) ?></p> - <div class="form-actions"> - <button type="submit" class="btn btn-red"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> - </form> -</section> + <?= $this->modal->submitButtons(t('Yes'), 'red') ?> +</form> diff --git a/app/Template/category/edit.php b/app/Template/category/edit.php index d8ca313d..108826f3 100644 --- a/app/Template/category/edit.php +++ b/app/Template/category/edit.php @@ -2,8 +2,7 @@ <h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('CategoryController', 'update', array('project_id' => $project['id'], 'category_id' => $values['id'])) ?>" autocomplete="off"> - +<form method="post" action="<?= $this->url->href('CategoryController', 'update', array('project_id' => $project['id'], 'category_id' => $values['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -15,9 +14,5 @@ <?= $this->form->label(t('Description'), 'description') ?> <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'CategoryController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/category/index.php b/app/Template/category/index.php index 336b79a2..e93138fd 100644 --- a/app/Template/category/index.php +++ b/app/Template/category/index.php @@ -15,12 +15,10 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id)) ?> </li> <li> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id)) ?> </li> </ul> </div> diff --git a/app/Template/category/remove.php b/app/Template/category/remove.php index e7b9c9b4..79e8a560 100644 --- a/app/Template/category/remove.php +++ b/app/Template/category/remove.php @@ -1,17 +1,15 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('Remove a category') ?></h2> - </div> +<div class="page-header"> + <h2><?= t('Remove a category') ?></h2> +</div> - <div class="confirm"> - <p class="alert alert-info"> - <?= t('Do you really want to remove this category: "%s"?', $category['name']) ?> - </p> +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this category: "%s"?', $category['name']) ?> + </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'CategoryController', 'remove', array('project_id' => $project['id'], 'category_id' => $category['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'CategoryController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> - </div> -</section> + <?= $this->modal->confirmButtons( + 'CategoryController', + 'remove', + array('project_id' => $project['id'], 'category_id' => $category['id']) + ) ?> +</div> diff --git a/app/Template/column/create.php b/app/Template/column/create.php index f4cded52..aad9606b 100644 --- a/app/Template/column/create.php +++ b/app/Template/column/create.php @@ -1,8 +1,7 @@ <div class="page-header"> <h2><?= t('Add a new column') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - +<form method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> @@ -18,9 +17,5 @@ <?= $this->form->label(t('Description'), 'description') ?> <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 4)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="5"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'column', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/column/edit.php b/app/Template/column/edit.php index a72a5c84..a36c2e6c 100644 --- a/app/Template/column/edit.php +++ b/app/Template/column/edit.php @@ -2,7 +2,7 @@ <h2><?= t('Edit column "%s"', $column['title']) ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ColumnController', 'update', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('ColumnController', 'update', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -20,9 +20,5 @@ <?= $this->form->label(t('Description'), 'description') ?> <?= $this->form->textEditor('description', $values, $errors) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ColumnController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/column/index.php b/app/Template/column/index.php index 8c96a350..9e7ddfe8 100644 --- a/app/Template/column/index.php +++ b/app/Template/column/index.php @@ -2,8 +2,7 @@ <h2><?= t('Edit the board for "%s"', $project['name']) ?></h2> <ul> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a new column'), 'ColumnController', 'create', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new column'), 'ColumnController', 'create', array('project_id' => $project['id'])) ?> </li> </ul> </div> @@ -41,12 +40,10 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?> </li> <li> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?> </li> </ul> </div> diff --git a/app/Template/column/remove.php b/app/Template/column/remove.php index dc0a4712..4134b175 100644 --- a/app/Template/column/remove.php +++ b/app/Template/column/remove.php @@ -8,8 +8,9 @@ <?= t('This action will REMOVE ALL TASKS associated to this column!') ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ColumnController', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ColumnController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ColumnController', + 'remove', + array('project_id' => $project['id'], 'column_id' => $column['id']) + ) ?> </div> diff --git a/app/Template/column_move_restriction/create.php b/app/Template/column_move_restriction/create.php index 1eb6d539..852df971 100644 --- a/app/Template/column_move_restriction/create.php +++ b/app/Template/column_move_restriction/create.php @@ -1,24 +1,18 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('New drag and drop restriction for the role "%s"', $role['role']) ?></h2> - </div> - <form class="popover-form" method="post" action="<?= $this->url->href('ColumnMoveRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->hidden('role_id', $values) ?> +<div class="page-header"> + <h2><?= t('New drag and drop restriction for the role "%s"', $role['role']) ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ColumnMoveRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('project_id', $values) ?> + <?= $this->form->hidden('role_id', $values) ?> - <?= $this->form->label(t('Source column'), 'src_column_id') ?> - <?= $this->form->select('src_column_id', $columns, $values, $errors) ?> + <?= $this->form->label(t('Source column'), 'src_column_id') ?> + <?= $this->form->select('src_column_id', $columns, $values, $errors) ?> - <?= $this->form->label(t('Destination column'), 'dst_column_id') ?> - <?= $this->form->select('dst_column_id', $columns, $values, $errors) ?> + <?= $this->form->label(t('Destination column'), 'dst_column_id') ?> + <?= $this->form->select('dst_column_id', $columns, $values, $errors) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> - <p class="alert alert-info"><?= t('People belonging to this role will be able to move tasks only between the source and the destination column.') ?></p> - </form> -</section> + <p class="alert alert-info"><?= t('People belonging to this role will be able to move tasks only between the source and the destination column.') ?></p> +</form> diff --git a/app/Template/column_move_restriction/remove.php b/app/Template/column_move_restriction/remove.php index 1985e167..4902cd23 100644 --- a/app/Template/column_move_restriction/remove.php +++ b/app/Template/column_move_restriction/remove.php @@ -7,8 +7,9 @@ <?= t('Do you really want to remove this column restriction: "%s" to "%s"?', $restriction['src_column_title'], $restriction['dst_column_title']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ColumnMoveRestrictionController', 'remove', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ColumnMoveRestrictionController', + 'remove', + array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']) + ) ?> </div> diff --git a/app/Template/column_restriction/create.php b/app/Template/column_restriction/create.php index 982733b4..be158f19 100644 --- a/app/Template/column_restriction/create.php +++ b/app/Template/column_restriction/create.php @@ -1,22 +1,16 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('New column restriction for the role "%s"', $role['role']) ?></h2> - </div> - <form class="popover-form" method="post" action="<?= $this->url->href('ColumnRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->hidden('role_id', $values) ?> +<div class="page-header"> + <h2><?= t('New column restriction for the role "%s"', $role['role']) ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ColumnRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('project_id', $values) ?> + <?= $this->form->hidden('role_id', $values) ?> - <?= $this->form->label(t('Rule'), 'rule') ?> - <?= $this->form->select('rule', $rules, $values, $errors) ?> + <?= $this->form->label(t('Rule'), 'rule') ?> + <?= $this->form->select('rule', $rules, $values, $errors) ?> - <?= $this->form->label(t('Column'), 'column_id') ?> - <?= $this->form->select('column_id', $columns, $values, $errors) ?> + <?= $this->form->label(t('Column'), 'column_id') ?> + <?= $this->form->select('column_id', $columns, $values, $errors) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?> - </div> - </form> -</section> + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/column_restriction/remove.php b/app/Template/column_restriction/remove.php index 97650e2d..edbd9d6e 100644 --- a/app/Template/column_restriction/remove.php +++ b/app/Template/column_restriction/remove.php @@ -7,8 +7,9 @@ <?= t('Do you really want to remove this column restriction?') ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ColumnRestrictionController', 'remove', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ColumnRestrictionController', + 'remove', + array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']) + ) ?> </div> diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php index 02cb48eb..8a421759 100644 --- a/app/Template/comment/create.php +++ b/app/Template/comment/create.php @@ -1,16 +1,12 @@ <div class="page-header"> <h2><?= t('Add a comment') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', $values) ?> <?= $this->form->hidden('user_id', $values) ?> <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php index f295c8a7..04f6ffd4 100644 --- a/app/Template/comment/edit.php +++ b/app/Template/comment/edit.php @@ -2,7 +2,7 @@ <h2><?= t('Edit a comment') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('task_id', $values) ?> @@ -10,9 +10,5 @@ <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/comment/remove.php b/app/Template/comment/remove.php index 55587b67..03f16e00 100644 --- a/app/Template/comment/remove.php +++ b/app/Template/comment/remove.php @@ -13,9 +13,9 @@ 'hide_actions' => true )) ?> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'CommentController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'CommentController', + 'remove', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']) + ) ?> </div> diff --git a/app/Template/comment/show.php b/app/Template/comment/show.php index 16a807bc..caf28c3c 100644 --- a/app/Template/comment/show.php +++ b/app/Template/comment/show.php @@ -25,12 +25,10 @@ </li> <?php if ($editable && ($this->user->isAdmin() || $this->user->isCurrentUser($comment['user_id']))): ?> <li> - <i class="fa fa-remove fa-fw"></i> - <?= $this->url->link(t('remove'), 'CommentController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('edit'), 'CommentController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?> </li> <li> - <i class="fa fa-edit fa-fw"></i> - <?= $this->url->link(t('edit'), 'CommentController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('remove'), 'CommentController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/custom_filter/add.php b/app/Template/custom_filter/add.php index 3801cc30..0b91226a 100644 --- a/app/Template/custom_filter/add.php +++ b/app/Template/custom_filter/add.php @@ -12,7 +12,7 @@ <?= $this->form->label(t('Filter'), 'filter') ?> <?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <?= $this->form->checkbox('is_shared', t('Share with all project members'), 1) ?> <?php endif ?> diff --git a/app/Template/custom_filter/edit.php b/app/Template/custom_filter/edit.php index 26da8da2..b64dee53 100644 --- a/app/Template/custom_filter/edit.php +++ b/app/Template/custom_filter/edit.php @@ -2,8 +2,7 @@ <h2><?= t('Edit custom filter') ?></h2> </div> -<form class="form-popover" method="post" action="<?= $this->url->href('CustomFilterController', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off"> - +<form method="post" action="<?= $this->url->href('CustomFilterController', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -16,7 +15,7 @@ <?= $this->form->label(t('Filter'), 'filter') ?> <?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <?= $this->form->checkbox('is_shared', t('Share with all project members'), 1, $values['is_shared'] == 1) ?> <?php else: ?> <?= $this->form->hidden('is_shared', $values) ?> @@ -24,9 +23,5 @@ <?= $this->form->checkbox('append', t('Append filter (instead of replacement)'), 1, $values['append'] == 1) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'CustomFilterController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/custom_filter/index.php b/app/Template/custom_filter/index.php index dcab891b..95356e64 100644 --- a/app/Template/custom_filter/index.php +++ b/app/Template/custom_filter/index.php @@ -36,8 +36,8 @@ <div class="dropdown"> <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> - <li><?= $this->url->link(t('Remove'), 'CustomFilterController', 'confirm', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id']), false, 'popover') ?></li> - <li><?= $this->url->link(t('Edit'), 'CustomFilterController', 'edit', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id']), false, 'popover') ?></li> + <li><?= $this->modal->medium('edit', t('Edit'), 'CustomFilterController', 'edit', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?></li> + <li><?= $this->modal->confirm('trash-o', t('Remove'), 'CustomFilterController', 'confirm', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?></li> </ul> </div> <?php endif ?> diff --git a/app/Template/custom_filter/remove.php b/app/Template/custom_filter/remove.php index 609f19b2..1c576fa2 100644 --- a/app/Template/custom_filter/remove.php +++ b/app/Template/custom_filter/remove.php @@ -1,17 +1,15 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('Remove a custom filter') ?></h2> - </div> +<div class="page-header"> + <h2><?= t('Remove a custom filter') ?></h2> +</div> - <div class="confirm"> - <p class="alert alert-info"> - <?= t('Do you really want to remove this custom filter: "%s"?', $filter['name']) ?> - </p> +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this custom filter: "%s"?', $filter['name']) ?> + </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'CustomFilterController', 'remove', array('project_id' => $project['id'], 'filter_id' => $filter['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'CustomFilterController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> - </div> -</section> + <?= $this->modal->confirmButtons( + 'CustomFilterController', + 'remove', + array('project_id' => $project['id'], 'filter_id' => $filter['id']) + ) ?> +</div> diff --git a/app/Template/dashboard/layout.php b/app/Template/dashboard/layout.php index 795537a6..d5a2695c 100644 --- a/app/Template/dashboard/layout.php +++ b/app/Template/dashboard/layout.php @@ -3,14 +3,12 @@ <ul> <?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('New project'), 'ProjectCreationController', 'create', array(), false, 'popover') ?> + <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?> </li> <?php endif ?> <?php if ($this->app->config('disable_private_project', 0) == 0): ?> <li> - <i class="fa fa-lock fa-fw"></i> - <?= $this->url->link(t('New private project'), 'ProjectCreationController', 'createPrivate', array(), false, 'popover') ?> + <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?> </li> <?php endif ?> <li> diff --git a/app/Template/external_task_creation/step1.php b/app/Template/external_task_creation/step1.php index 2af9b427..415b02ef 100644 --- a/app/Template/external_task_creation/step1.php +++ b/app/Template/external_task_creation/step1.php @@ -1,4 +1,4 @@ -<form class="popover-form" method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step2', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>"> +<form method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step2', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>"> <?= $this->form->csrf() ?> <?= $this->form->hidden('swimlane_id', $values) ?> <?= $this->form->hidden('column_id', $values) ?> @@ -12,8 +12,5 @@ <div class="alert alert-error"><?= $this->text->e($error_message) ?></div> <?php endif ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Next') ?></button> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons(t('Next')) ?> </form> diff --git a/app/Template/external_task_creation/step2.php b/app/Template/external_task_creation/step2.php index 4bc0e509..baace3ae 100644 --- a/app/Template/external_task_creation/step2.php +++ b/app/Template/external_task_creation/step2.php @@ -1,4 +1,4 @@ -<form class="popover-form" method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step3', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>"> +<form method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step3', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>"> <?= $this->form->csrf() ?> <?= $this->form->hidden('external_provider', $values) ?> <?= $this->form->hidden('external_uri', $values) ?> @@ -18,8 +18,5 @@ <div class="alert alert-error"><?= $this->text->e($error_message) ?></div> <?php endif ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/external_task_modification/show.php b/app/Template/external_task_modification/show.php index 79dd9556..55180b96 100644 --- a/app/Template/external_task_modification/show.php +++ b/app/Template/external_task_modification/show.php @@ -1,4 +1,4 @@ -<form class="popover-form" method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $project['id'])) ?>"> +<form method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $project['id'])) ?>"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> @@ -18,9 +18,5 @@ )) ?> <?php endif ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/group/associate.php b/app/Template/group/associate.php index 24edd133..1840816d 100644 --- a/app/Template/group/associate.php +++ b/app/Template/group/associate.php @@ -4,7 +4,7 @@ <?php if (empty($users)): ?> <p class="alert"><?= t('There is no user available.') ?></p> <?php else: ?> - <form class="popover-form" method="post" action="<?= $this->url->href('GroupListController', 'addUser', array('group_id' => $group['id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('GroupListController', 'addUser', array('group_id' => $group['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('group_id', $values) ?> @@ -15,10 +15,6 @@ 'defaultValue' => isset($values['user_id']) ? $values['user_id'] : null, )) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/group/dissociate.php b/app/Template/group/dissociate.php index 50ef6d61..24836397 100644 --- a/app/Template/group/dissociate.php +++ b/app/Template/group/dissociate.php @@ -4,9 +4,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to remove the user "%s" from the group "%s"?', $user['name'] ?: $user['username'], $group['name']) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'GroupListController', 'removeUser', array('group_id' => $group['id'], 'user_id' => $user['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'GroupListController', 'users', array('group_id' => $group['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'GroupListController', + 'removeUser', + array('group_id' => $group['id'], 'user_id' => $user['id']) + ) ?> </div> diff --git a/app/Template/group/index.php b/app/Template/group/index.php index fe8a07e7..8001393b 100644 --- a/app/Template/group/index.php +++ b/app/Template/group/index.php @@ -2,7 +2,7 @@ <div class="page-header"> <ul> <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?></li> - <li><i class="fa fa-user-plus fa-fw"></i><?= $this->url->link(t('New group'), 'GroupCreationController', 'show', array(), false, 'popover') ?></li> + <li><?= $this->modal->medium('user-plus', t('New group'), 'GroupCreationController', 'show') ?></li> </ul> </div> <?php if ($paginator->isEmpty()): ?> @@ -30,10 +30,10 @@ <div class="dropdown"> <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> - <li><?= $this->url->link(t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id']), false, 'popover') ?></li> - <li><?= $this->url->link(t('Members'), 'GroupListController', 'users', array('group_id' => $group['id'])) ?></li> - <li><?= $this->url->link(t('Edit'), 'GroupModificationController', 'show', array('group_id' => $group['id']), false, 'popover') ?></li> - <li><?= $this->url->link(t('Remove'), 'GroupListController', 'confirm', array('group_id' => $group['id']), false, 'popover') ?></li> + <li><?= $this->modal->medium('plus', t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id'])) ?></li> + <li><i class="fa fa-users fa-fw"></i> <?= $this->url->link(t('Members'), 'GroupListController', 'users', array('group_id' => $group['id'])) ?></li> + <li><?= $this->modal->medium('edit', t('Edit'), 'GroupModificationController', 'show', array('group_id' => $group['id'])) ?></li> + <li><?= $this->modal->confirm('trash-o', t('Remove'), 'GroupListController', 'confirm', array('group_id' => $group['id'])) ?></li> </ul> </div> </td> diff --git a/app/Template/group/remove.php b/app/Template/group/remove.php index 408b3d83..77d602f9 100644 --- a/app/Template/group/remove.php +++ b/app/Template/group/remove.php @@ -4,9 +4,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to remove this group: "%s"?', $group['name']) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'GroupListController', 'remove', array('group_id' => $group['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'GroupListController', + 'remove', + array('group_id' => $group['id']) + ) ?> </div> diff --git a/app/Template/group/users.php b/app/Template/group/users.php index 73597b39..e9f2b361 100644 --- a/app/Template/group/users.php +++ b/app/Template/group/users.php @@ -2,7 +2,7 @@ <div class="page-header"> <ul> <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id']), false, 'popover') ?></li> + <li><?= $this->modal->medium('plus', t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id'])) ?></li> </ul> </div> <?php if ($paginator->isEmpty()): ?> @@ -31,8 +31,7 @@ <a href="mailto:<?= $this->text->e($user['email']) ?>"><?= $this->text->e($user['email']) ?></a> </td> <td> - <i class="fa fa-times fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove this user'), 'GroupListController', 'dissociate', array('group_id' => $group['id'], 'user_id' => $user['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove this user'), 'GroupListController', 'dissociate', array('group_id' => $group['id'], 'user_id' => $user['id'])) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/group_creation/show.php b/app/Template/group_creation/show.php index b219bd70..9f4f5608 100644 --- a/app/Template/group_creation/show.php +++ b/app/Template/group_creation/show.php @@ -1,15 +1,11 @@ <div class="page-header"> <h2><?= t('New group') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('GroupCreationController', 'save') ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('GroupCreationController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/group_modification/show.php b/app/Template/group_modification/show.php index ddf07369..df4ed01e 100644 --- a/app/Template/group_modification/show.php +++ b/app/Template/group_modification/show.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Edit group') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('GroupModificationController', 'save') ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('GroupModificationController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -10,9 +10,5 @@ <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/header/creation_dropdown.php b/app/Template/header/creation_dropdown.php index d3b9e7cb..9bdf5ad2 100644 --- a/app/Template/header/creation_dropdown.php +++ b/app/Template/header/creation_dropdown.php @@ -6,14 +6,13 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-plus fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <?php if ($has_project_creation_access): ?> - <li><i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('New project'), 'ProjectCreationController', 'create', array(), false, 'popover') ?> + <li> + <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?> </li> <?php endif ?> <?php if ($is_private_project_enabled): ?> <li> - <i class="fa fa-lock fa-fw"></i> - <?= $this->url->link(t('New private project'), 'ProjectCreationController', 'createPrivate', array(), false, 'popover') ?> + <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?> </li> <?php endif ?> <?= $this->hook->render('template:header:creation-dropdown') ?> diff --git a/app/Template/link/remove.php b/app/Template/link/remove.php index b7fbef5e..e5ea2466 100644 --- a/app/Template/link/remove.php +++ b/app/Template/link/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this link: "%s"?', $link['label']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'LinkController', 'remove', array('link_id' => $link['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'LinkController', 'index') ?> - </div> + <?= $this->modal->confirmButtons( + 'LinkController', + 'remove', + array('link_id' => $link['id']) + ) ?> </div> diff --git a/app/Template/plugin/remove.php b/app/Template/plugin/remove.php index bd8f4eb8..1280f8aa 100644 --- a/app/Template/plugin/remove.php +++ b/app/Template/plugin/remove.php @@ -5,9 +5,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to remove this plugin: "%s"?', $plugin->getPluginName()) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'PluginController', 'uninstall', array('pluginId' => $plugin_id), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'PluginController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'PluginController', + 'uninstall', + array('pluginId' => $plugin_id) + ) ?> </div> diff --git a/app/Template/plugin/show.php b/app/Template/plugin/show.php index 9c3d6d20..0e997c55 100644 --- a/app/Template/plugin/show.php +++ b/app/Template/plugin/show.php @@ -28,8 +28,7 @@ <td><?= $this->text->e($plugin->getPluginVersion()) ?></td> <?php if ($is_configured): ?> <td> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?> </td> <?php endif ?> </tr> diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php index e3cf41c2..5fbffde4 100644 --- a/app/Template/project/dropdown.php +++ b/app/Template/project/dropdown.php @@ -34,7 +34,7 @@ <?= $this->hook->render('template:project:dropdown', array('project' => $project)) ?> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <li> <i class="fa fa-cog fa-fw"></i> <?= $this->url->link(t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?> diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php index 6e7fff05..507cde3e 100644 --- a/app/Template/project/sidebar.php +++ b/app/Template/project/sidebar.php @@ -9,9 +9,9 @@ </li> <?php endif ?> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <li <?= $this->app->checkMenuSelection('ProjectEditController') ?>> - <?= $this->url->link(t('Edit project'), 'ProjectEditController', 'edit', array('project_id' => $project['id'])) ?> + <?= $this->url->link(t('Edit project'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?> </li> <li <?= $this->app->checkMenuSelection('ProjectViewController', 'share') ?>> <?= $this->url->link(t('Public access'), 'ProjectViewController', 'share', array('project_id' => $project['id'])) ?> @@ -50,15 +50,15 @@ </li> <?php if ($project['is_active']): ?> <li> - <?= $this->url->link(t('Disable'), 'ProjectStatusController', 'confirmDisable', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->confirm('close', t('Disable'), 'ProjectStatusController', 'confirmDisable', array('project_id' => $project['id'])) ?> <?php else: ?> <li> - <?= $this->url->link(t('Enable'), 'ProjectStatusController', 'confirmEnable', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->confirm('check-square-o', t('Enable'), 'ProjectStatusController', 'confirmEnable', array('project_id' => $project['id'])) ?> <?php endif ?> </li> <?php if ($this->user->hasProjectAccess('ProjectStatusController', 'remove', $project['id'])): ?> <li> - <?= $this->url->link(t('Remove'), 'ProjectStatusController', 'confirmRemove', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectStatusController', 'confirmRemove', array('project_id' => $project['id'])) ?> </li> <?php endif ?> <?php endif ?> diff --git a/app/Template/project_action_duplication/show.php b/app/Template/project_action_duplication/show.php index 2eebb262..c2f52e35 100644 --- a/app/Template/project_action_duplication/show.php +++ b/app/Template/project_action_duplication/show.php @@ -4,16 +4,12 @@ <?php if (empty($projects_list)): ?> <p class="alert"><?= t('There is no available project.') ?></p> <?php else: ?> - <form class="popover-form" method="post" action="<?= $this->url->href('ProjectActionDuplicationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('ProjectActionDuplicationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->label(t('Create from another project'), 'src_project_id') ?> <?= $this->form->select('src_project_id', $projects_list) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'Action', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php index 1df15d7d..171bd17a 100644 --- a/app/Template/project_creation/create.php +++ b/app/Template/project_creation/create.php @@ -2,7 +2,7 @@ <div class="page-header"> <h2><?= $title ?></h2> </div> - <form class="popover-form" id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreationController', 'save') ?>" autocomplete="off"> + <form id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreationController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('is_private', $values) ?> @@ -29,11 +29,7 @@ <?= $this->form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?> </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php if ($is_private): ?> <div class="alert alert-info"> diff --git a/app/Template/project_edit/dates.php b/app/Template/project_edit/dates.php deleted file mode 100644 index 1c2c3dd4..00000000 --- a/app/Template/project_edit/dates.php +++ /dev/null @@ -1,22 +0,0 @@ -<div class="page-header"> - <h2><?= t('Edit project') ?></h2> - <ul> - <li ><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li class="active"><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li> - </ul> -</div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'dates')) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('name', $values) ?> - <?= $this->form->date(t('Start date'), 'start_date', $values, $errors) ?> - <?= $this->form->date(t('End date'), 'end_date', $values, $errors) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> - -<p class="alert alert-info"><?= t('Those dates are useful for the project Gantt chart.') ?></p> diff --git a/app/Template/project_edit/description.php b/app/Template/project_edit/description.php deleted file mode 100644 index 586ddfcb..00000000 --- a/app/Template/project_edit/description.php +++ /dev/null @@ -1,19 +0,0 @@ -<div class="page-header"> - <h2><?= t('Edit project') ?></h2> - <ul> - <li><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li class="active"><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li> - </ul> -</div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'description')) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('name', $values) ?> - <?= $this->form->textEditor('description', $values, $errors, array('autofocus' => true)) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> diff --git a/app/Template/project_edit/general.php b/app/Template/project_edit/general.php deleted file mode 100644 index c7421477..00000000 --- a/app/Template/project_edit/general.php +++ /dev/null @@ -1,36 +0,0 @@ -<div class="page-header"> - <h2><?= t('Edit project') ?></h2> - <ul> - <li class="active"><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li> - </ul> -</div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - - <?= $this->form->label(t('Name'), 'name') ?> - <?= $this->form->text('name', $values, $errors, array('required', 'maxlength="50"')) ?> - - <?= $this->form->label(t('Identifier'), 'identifier') ?> - <?= $this->form->text('identifier', $values, $errors, array('maxlength="50"')) ?> - <p class="form-help"><?= t('The project identifier is optional and must be alphanumeric, example: MYPROJECT.') ?></p> - - <hr> - <div class="form-inline"> - <?= $this->form->label(t('Project owner'), 'owner_id') ?> - <?= $this->form->select('owner_id', $owners, $values, $errors) ?> - </div> - - <?php if ($this->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])): ?> - <hr> - <?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?> - <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p> - <?php endif ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> diff --git a/app/Template/project_edit/show.php b/app/Template/project_edit/show.php new file mode 100644 index 00000000..f574b6f9 --- /dev/null +++ b/app/Template/project_edit/show.php @@ -0,0 +1,57 @@ +<div class="page-header"> + <h2><?= $this->text->e($project['name']) ?> > <?= t('Edit project') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('id', $values) ?> + + <fieldset> + <legend><?= t('General') ?></legend> + + <?= $this->form->label(t('Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('required', 'maxlength="50"', 'autofocus', 'tabindex="1"')) ?> + + <?= $this->form->label(t('Identifier'), 'identifier') ?> + <?= $this->form->text('identifier', $values, $errors, array('maxlength="50"', 'tabindex="2"')) ?> + <p class="form-help"><?= t('The project identifier is optional and must be alphanumeric, example: MYPROJECT.') ?></p> + + <?= $this->form->label(t('Description'), 'description') ?> + <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 3)) ?> + </fieldset> + + <fieldset> + <legend><?= t('Permissions and ownership') ?></legend> + + <?php if ($this->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])): ?> + <?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?> + <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p> + <?php endif ?> + + <div class="form-inline"> + <?= $this->form->label(t('Project owner'), 'owner_id') ?> + <?= $this->form->select('owner_id', $owners, $values, $errors, array('tabindex="5"')) ?> + </div> + </fieldset> + + <fieldset> + <legend><?= t('Dates') ?></legend> + + <?= $this->form->date(t('Start date'), 'start_date', $values, $errors, array('tabindex="6"')) ?> + <?= $this->form->date(t('End date'), 'end_date', $values, $errors, array('tabindex="7"')) ?> + </fieldset> + + <fieldset> + <legend><?= t('Priorities') ?></legend> + + <?= $this->form->label(t('Default priority'), 'priority_default') ?> + <?= $this->form->number('priority_default', $values, $errors, array('tabindex="8"')) ?> + + <?= $this->form->label(t('Lowest priority'), 'priority_start') ?> + <?= $this->form->number('priority_start', $values, $errors, array('tabindex="9"')) ?> + + <?= $this->form->label(t('Highest priority'), 'priority_end') ?> + <?= $this->form->number('priority_end', $values, $errors, array('tabindex="10"')) ?> + </fieldset> + + <?= $this->modal->submitButtons(array('tabindex' => 11)) ?> +</form> diff --git a/app/Template/project_edit/task_priority.php b/app/Template/project_edit/task_priority.php deleted file mode 100644 index 3ef4b3cb..00000000 --- a/app/Template/project_edit/task_priority.php +++ /dev/null @@ -1,29 +0,0 @@ -<div class="page-header"> - <h2><?= t('Edit project') ?></h2> - <ul> - <li ><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li> - <li class="active"><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li> - </ul> -</div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'priority')) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('id', $values) ?> - <?= $this->form->hidden('name', $values) ?> - - <?= $this->form->label(t('Default priority'), 'priority_default') ?> - <?= $this->form->number('priority_default', $values, $errors) ?> - - <?= $this->form->label(t('Lowest priority'), 'priority_start') ?> - <?= $this->form->number('priority_start', $values, $errors) ?> - - <?= $this->form->label(t('Highest priority'), 'priority_end') ?> - <?= $this->form->number('priority_end', $values, $errors) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - </div> -</form> - -<p class="alert alert-info"><?= t('If you put zero to the low and high priority, this feature will be disabled.') ?></p> diff --git a/app/Template/project_file/create.php b/app/Template/project_file/create.php index e262799b..de35f87c 100644 --- a/app/Template/project_file/create.php +++ b/app/Template/project_file/create.php @@ -1,33 +1,20 @@ <div class="page-header"> <h2><?= t('Attach a document') ?></h2> </div> -<div id="file-done" style="display:none"> - <p class="alert alert-success"> - <?= t('All files have been uploaded successfully.') ?> - <?= $this->url->link(t('View uploaded files'), 'ProjectOverviewController', 'show', array('project_id' => $project['id'])) ?> - </p> -</div> - -<div id="file-error-max-size" style="display:none"> - <p class="alert alert-error"> - <?= t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) ?> - <a href="#" id="file-browser"><?= t('Choose files again') ?></a> - </p> -</div> -<div - id="file-dropzone" - data-max-size="<?= $max_size ?>" - data-url="<?= $this->url->href('ProjectFileController', 'save', array('project_id' => $project['id'])) ?>"> - <div id="file-dropzone-inner"> - <?= t('Drag and drop your files here') ?> <?= t('or') ?> <a href="#" id="file-browser"><?= t('choose files') ?></a> - </div> -</div> - -<input type="file" name="files[]" multiple style="display:none" id="file-form-element"> +<?= $this->app->component('file-upload', array( + 'maxSize' => $max_size, + 'url' => $this->url->to('ProjectFileController', 'save', array('project_id' => $project['id'])), + 'labelDropzone' => t('Drag and drop your files here'), + 'labelOr' => t('or'), + 'labelChooseFiles' => t('choose files'), + 'labelOversize' => t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)), + 'labelSuccess' => t('All files have been uploaded successfully.'), + 'labelCloseSuccess' => t('Close this window'), + 'labelUploadError' => t('Unable to upload this file.'), +)) ?> -<div class="form-actions"> - <input type="submit" value="<?= t('Upload files') ?>" class="btn btn-blue" id="file-upload-button" disabled> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectOverviewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> -</div> +<?= $this->modal->submitButtons(array( + 'submitLabel' => t('Upload files'), + 'disabled' => true, +)) ?> diff --git a/app/Template/project_file/remove.php b/app/Template/project_file/remove.php index 0517a9e7..043b8fc8 100644 --- a/app/Template/project_file/remove.php +++ b/app/Template/project_file/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this file: "%s"?', $this->text->e($file['name'])) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectFileController', 'remove', array('project_id' => $project['id'], 'file_id' => $file['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectOverviewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectFileController', + 'remove', + array('project_id' => $project['id'], 'file_id' => $file['id']) + ) ?> </div> diff --git a/app/Template/project_header/dropdown.php b/app/Template/project_header/dropdown.php index baf4cc16..27d3fd3f 100644 --- a/app/Template/project_header/dropdown.php +++ b/app/Template/project_header/dropdown.php @@ -24,8 +24,7 @@ <?php if ($this->user->hasProjectAccess('TaskCreationController', 'show', $project['id'])): ?> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->large('plus', t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $project['id'])) ?> </li> <?php endif ?> @@ -71,7 +70,7 @@ </li> <?php endif ?> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <li> <i class="fa fa-cog fa-fw"></i> <?= $this->url->link(t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?> diff --git a/app/Template/project_overview/attachments.php b/app/Template/project_overview/attachments.php index ab8cf2ad..b8baadd0 100644 --- a/app/Template/project_overview/attachments.php +++ b/app/Template/project_overview/attachments.php @@ -5,7 +5,7 @@ <div class="accordion-content"> <?php if ($this->user->hasProjectAccess('ProjectFileController', 'create', $project['id'])): ?> <div class="buttons-header"> - <?= $this->url->button('fa-plus', t('Upload a file'), 'ProjectFileController', 'create', array('project_id' => $project['id']), 'popover') ?> + <?= $this->modal->mediumButton('plus', t('Upload a file'), 'ProjectFileController', 'create', array('project_id' => $project['id'])) ?> </div> <?php endif ?> diff --git a/app/Template/project_overview/description.php b/app/Template/project_overview/description.php index 0c2027ed..80b93efe 100644 --- a/app/Template/project_overview/description.php +++ b/app/Template/project_overview/description.php @@ -3,9 +3,9 @@ <h3><a href="#" class="fa accordion-toggle"></a> <?= t('Description') ?></h3> </div> <div class="accordion-content"> - <?php if ($this->user->hasProjectAccess('ProjectEditController', 'description', $project['id'])): ?> + <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> <div class="buttons-header"> - <?= $this->url->button('fa-edit', t('Edit description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), 'popover') ?> + <?= $this->modal->mediumButton('edit', t('Edit description'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?> </div> <?php endif ?> <article class="markdown"> diff --git a/app/Template/project_overview/files.php b/app/Template/project_overview/files.php index 1326a7d1..6518fafb 100644 --- a/app/Template/project_overview/files.php +++ b/app/Template/project_overview/files.php @@ -15,8 +15,7 @@ <ul> <?php if ($this->file->getPreviewType($file['name']) !== null): ?> <li> - <i class="fa fa-eye fa-fw"></i> - <?= $this->url->link(t('View file'), 'FileViewerController', 'show', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->large('eye', t('View file'), 'FileViewerController', 'show', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?> </li> <?php elseif ($this->file->getBrowserViewType($file['name']) !== null): ?> <li> @@ -30,8 +29,7 @@ </li> <?php if ($this->user->hasProjectAccess('ProjectFileController', 'remove', $project['id'])): ?> <li> - <i class="fa fa-trash fa-fw"></i> - <?= $this->url->link(t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/project_overview/images.php b/app/Template/project_overview/images.php index 3b575d3f..cabfd0f6 100644 --- a/app/Template/project_overview/images.php +++ b/app/Template/project_overview/images.php @@ -24,8 +24,7 @@ </li> <?php if ($this->user->hasProjectAccess('ProjectFileController', 'remove', $project['id'])): ?> <li> - <i class="fa fa-trash fa-fw"></i> - <?= $this->url->link(t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/project_role/create.php b/app/Template/project_role/create.php index d0092243..f554eb17 100644 --- a/app/Template/project_role/create.php +++ b/app/Template/project_role/create.php @@ -1,16 +1,12 @@ <div class="page-header"> <h2><?= t('New custom project role') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ProjectRoleController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('ProjectRoleController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Role'), 'role') ?> <?= $this->form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/project_role/edit.php b/app/Template/project_role/edit.php index 3aa9e5cf..740ac0fe 100644 --- a/app/Template/project_role/edit.php +++ b/app/Template/project_role/edit.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Edit custom project role') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('ProjectRoleController', 'update', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('ProjectRoleController', 'update', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->hidden('role_id', $values) ?> @@ -9,9 +9,5 @@ <?= $this->form->label(t('Role'), 'role') ?> <?= $this->form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/project_role/remove.php b/app/Template/project_role/remove.php index 25875e3a..44d24eda 100644 --- a/app/Template/project_role/remove.php +++ b/app/Template/project_role/remove.php @@ -7,8 +7,9 @@ <?= t('Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.', $role['role']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectRoleController', 'remove', array('project_id' => $project['id'], 'role_id' => $role['role_id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectRoleController', + 'remove', + array('project_id' => $project['id'], 'role_id' => $role['role_id']) + ) ?> </div> diff --git a/app/Template/project_role/show.php b/app/Template/project_role/show.php index 59200fc9..5377f7bb 100644 --- a/app/Template/project_role/show.php +++ b/app/Template/project_role/show.php @@ -2,8 +2,7 @@ <h2><?= t('Custom Project Roles') ?></h2> <ul> <li> - <i class="fa fa-plus fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Add a new custom role'), 'ProjectRoleController', 'create', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new custom role'), 'ProjectRoleController', 'create', array('project_id' => $project['id'])) ?> </li> </ul> </div> @@ -19,24 +18,19 @@ <a href="#" class="dropdown-menu"><?= t('Restrictions for the role "%s"', $role['role']) ?> <i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-plus fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Add a new project restriction'), 'ProjectRoleRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new project restriction'), 'ProjectRoleRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?> </li> <li> - <i class="fa fa-plus fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Add a new drag and drop restriction'), 'ColumnMoveRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new drag and drop restriction'), 'ColumnMoveRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?> </li> <li> - <i class="fa fa-plus fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Add a new column restriction'), 'ColumnRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new column restriction'), 'ColumnRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?> </li> <li> - <i class="fa fa-pencil fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Edit this role'), 'ProjectRoleController', 'edit', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit this role'), 'ProjectRoleController', 'edit', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?> </li> <li> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove this role'), 'ProjectRoleController', 'confirm', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove this role'), 'ProjectRoleController', 'confirm', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?> </li> </ul> </div> @@ -59,8 +53,7 @@ <?= $this->text->e($restriction['title']) ?> </td> <td> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'ProjectRoleRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectRoleRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> </td> </tr> <?php endforeach ?> @@ -77,8 +70,7 @@ <?= $this->text->e($restriction['title']) ?> </td> <td> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'ColumnRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> </td> </tr> <?php endforeach ?> @@ -91,8 +83,7 @@ <?= t('Only moving task between those columns is permitted') ?> </td> <td> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'ColumnMoveRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnMoveRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/project_role_restriction/create.php b/app/Template/project_role_restriction/create.php index f49eafb3..2b6a61dc 100644 --- a/app/Template/project_role_restriction/create.php +++ b/app/Template/project_role_restriction/create.php @@ -2,7 +2,7 @@ <div class="page-header"> <h2><?= t('New project restriction for the role "%s"', $role['role']) ?></h2> </div> - <form class="popover-form" method="post" action="<?= $this->url->href('ProjectRoleRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('ProjectRoleRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->hidden('role_id', $values) ?> @@ -10,10 +10,6 @@ <?= $this->form->label(t('Restriction'), 'rule') ?> <?= $this->form->select('rule', $restrictions, $values, $errors) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> </section> diff --git a/app/Template/project_role_restriction/remove.php b/app/Template/project_role_restriction/remove.php index db1148e1..1a994199 100644 --- a/app/Template/project_role_restriction/remove.php +++ b/app/Template/project_role_restriction/remove.php @@ -7,8 +7,9 @@ <?= t('Do you really want to remove this project restriction: "%s"?', $this->text->in($restriction['rule'], $restrictions)) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectRoleRestrictionController', 'remove', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectRoleRestrictionController', + 'remove', + array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']) + ) ?> </div> diff --git a/app/Template/project_status/disable.php b/app/Template/project_status/disable.php index d8145d3c..d607cedb 100644 --- a/app/Template/project_status/disable.php +++ b/app/Template/project_status/disable.php @@ -7,8 +7,9 @@ <?= t('Do you really want to disable this project: "%s"?', $project['name']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'disable', array('project_id' => $project['id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectStatusController', + 'disable', + array('project_id' => $project['id']) + ) ?> </div> diff --git a/app/Template/project_status/enable.php b/app/Template/project_status/enable.php index 1f76d093..fd8f8c72 100644 --- a/app/Template/project_status/enable.php +++ b/app/Template/project_status/enable.php @@ -7,8 +7,9 @@ <?= t('Do you really want to enable this project: "%s"?', $project['name']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'enable', array('project_id' => $project['id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectStatusController', + 'enable', + array('project_id' => $project['id']) + ) ?> </div> diff --git a/app/Template/project_status/remove.php b/app/Template/project_status/remove.php index 8959ef75..27ae2ae0 100644 --- a/app/Template/project_status/remove.php +++ b/app/Template/project_status/remove.php @@ -7,8 +7,9 @@ <?= t('Do you really want to remove this project: "%s"?', $project['name']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'remove', array('project_id' => $project['id']), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectStatusController', + 'remove', + array('project_id' => $project['id']) + ) ?> </div> diff --git a/app/Template/project_tag/create.php b/app/Template/project_tag/create.php index bfd1084a..a0e6243b 100644 --- a/app/Template/project_tag/create.php +++ b/app/Template/project_tag/create.php @@ -1,16 +1,12 @@ <div class="page-header"> <h2><?= t('Add new tag') ?></h2> </div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectTagController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('ProjectTagController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/project_tag/edit.php b/app/Template/project_tag/edit.php index 9bf261bd..8cb1e209 100644 --- a/app/Template/project_tag/edit.php +++ b/app/Template/project_tag/edit.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Edit a tag') ?></h2> </div> -<form method="post" class="popover-form" action="<?= $this->url->href('ProjectTagController', 'update', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('ProjectTagController', 'update', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> @@ -9,9 +9,5 @@ <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/project_tag/index.php b/app/Template/project_tag/index.php index f77e21ee..29d7082b 100644 --- a/app/Template/project_tag/index.php +++ b/app/Template/project_tag/index.php @@ -2,8 +2,7 @@ <h2><?= t('Project tags') ?></h2> <ul> <li> - <i class="fa fa-plus" aria-hidden="true"></i> - <?= $this->url->link(t('Add new tag'), 'ProjectTagController', 'create', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add new tag'), 'ProjectTagController', 'create', array('project_id' => $project['id'])) ?> </li> </ul> </div> @@ -20,10 +19,8 @@ <tr> <td><?= $this->text->e($tag['name']) ?></td> <td> - <i class="fa fa-times" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'ProjectTagController', 'confirm', array('tag_id' => $tag['id'], 'project_id' => $project['id']), false, 'popover') ?> - <i class="fa fa-pencil-square-o" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'ProjectTagController', 'edit', array('tag_id' => $tag['id'], 'project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'ProjectTagController', 'edit', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectTagController', 'confirm', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/project_tag/remove.php b/app/Template/project_tag/remove.php index f4aadab1..9f957d10 100644 --- a/app/Template/project_tag/remove.php +++ b/app/Template/project_tag/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this tag: "%s"?', $tag['name']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'ProjectTagController', 'remove', array('tag_id' => $tag['id'], 'project_id' => $project['id']), true, 'btn btn-red popover-link') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'ProjectTagController', + 'remove', + array('tag_id' => $tag['id'], 'project_id' => $project['id']) + ) ?> </div> diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php index cc4ccba6..ade9686e 100644 --- a/app/Template/subtask/create.php +++ b/app/Template/subtask/create.php @@ -2,7 +2,7 @@ <h2><?= t('Add a sub-task') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', $values) ?> @@ -13,9 +13,5 @@ <?= $this->form->checkbox('another_subtask', t('Create another sub-task'), 1, isset($values['another_subtask']) && $values['another_subtask'] == 1) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php index 07419f79..b34b93a8 100644 --- a/app/Template/subtask/edit.php +++ b/app/Template/subtask/edit.php @@ -2,8 +2,7 @@ <h2><?= t('Edit a sub-task') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off"> - +<form method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('task_id', $values) ?> @@ -12,10 +11,6 @@ <?= $this->subtask->selectTimeEstimated($values, $errors) ?> <?= $this->subtask->selectTimeSpent($values, $errors) ?> <?= $this->hook->render('template:subtask:form:edit', array('values' => $values, 'errors' => $errors)) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/subtask/menu.php b/app/Template/subtask/menu.php index d5d1bf85..a0743a70 100644 --- a/app/Template/subtask/menu.php +++ b/app/Template/subtask/menu.php @@ -2,16 +2,13 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-pencil-square-o" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'SubtaskController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'SubtaskController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> </li> <li> - <i class="fa fa-trash-o" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'SubtaskController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'SubtaskController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> </li> <li> - <i class="fa fa-clone" aria-hidden="true"></i> - <?= $this->url->link(t('Convert to task'), 'SubtaskConverterController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?> + <?= $this->modal->confirm('clone', t('Convert to task'), 'SubtaskConverterController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?> </li> </ul> </div> diff --git a/app/Template/subtask/remove.php b/app/Template/subtask/remove.php index 426c1a93..cf9bbc35 100644 --- a/app/Template/subtask/remove.php +++ b/app/Template/subtask/remove.php @@ -12,9 +12,9 @@ </ul> </div> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'SubtaskController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'SubtaskController', + 'remove', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']) + ) ?> </div> diff --git a/app/Template/subtask_converter/show.php b/app/Template/subtask_converter/show.php index 63f45482..9ecc70c8 100644 --- a/app/Template/subtask_converter/show.php +++ b/app/Template/subtask_converter/show.php @@ -12,9 +12,9 @@ </ul> </div> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'SubtaskConverterController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'SubtaskConverterController', + 'save', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']) + ) ?> </div> diff --git a/app/Template/subtask_restriction/show.php b/app/Template/subtask_restriction/show.php index ec8b8d5b..b6c56a12 100644 --- a/app/Template/subtask_restriction/show.php +++ b/app/Template/subtask_restriction/show.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('You already have one subtask in progress') ?></h2> </div> -<form class="popover-form" action="<?= $this->url->href('SubtaskRestrictionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post"> +<form action="<?= $this->url->href('SubtaskRestrictionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post"> <?= $this->form->csrf() ?> @@ -9,9 +9,5 @@ <?= $this->form->radios('status', $status_list) ?> <?= $this->form->hidden('id', $subtask_inprogress) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-red"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/swimlane/create.php b/app/Template/swimlane/create.php index 207b526c..7d05e731 100644 --- a/app/Template/swimlane/create.php +++ b/app/Template/swimlane/create.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Add a new swimlane') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('SwimlaneController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> @@ -12,9 +12,5 @@ <?= $this->form->label(t('Description'), 'description') ?> <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="3"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/swimlane/edit.php b/app/Template/swimlane/edit.php index d225b345..c1c41196 100644 --- a/app/Template/swimlane/edit.php +++ b/app/Template/swimlane/edit.php @@ -2,7 +2,7 @@ <h2><?= t('Swimlane modification for the project "%s"', $project['name']) ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'update', array('project_id' => $project['id'], 'swimlane_id' => $values['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('SwimlaneController', 'update', array('project_id' => $project['id'], 'swimlane_id' => $values['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -15,9 +15,5 @@ <?= $this->form->label(t('Description'), 'description') ?> <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="3"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/swimlane/edit_default.php b/app/Template/swimlane/edit_default.php index 8a0c0a15..a2c3ee73 100644 --- a/app/Template/swimlane/edit_default.php +++ b/app/Template/swimlane/edit_default.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Change default swimlane') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'updateDefault', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('SwimlaneController', 'updateDefault', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> @@ -10,9 +10,5 @@ <?= $this->form->checkbox('show_default_swimlane', t('Show default swimlane'), 1, $values['show_default_swimlane'] == 1) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/swimlane/index.php b/app/Template/swimlane/index.php index 4f78a405..e05b9088 100644 --- a/app/Template/swimlane/index.php +++ b/app/Template/swimlane/index.php @@ -2,8 +2,7 @@ <h2><?= t('Swimlanes') ?></h2> <ul> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a new swimlane'), 'SwimlaneController', 'create', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a new swimlane'), 'SwimlaneController', 'create', array('project_id' => $project['id'])) ?> </li> </ul> </div> diff --git a/app/Template/swimlane/remove.php b/app/Template/swimlane/remove.php index f16b778c..02d1e322 100644 --- a/app/Template/swimlane/remove.php +++ b/app/Template/swimlane/remove.php @@ -1,17 +1,15 @@ -<section id="main"> - <div class="page-header"> - <h2><?= t('Remove a swimlane') ?></h2> - </div> +<div class="page-header"> + <h2><?= t('Remove a swimlane') ?></h2> +</div> - <div class="confirm"> - <p class="alert alert-info"> - <?= t('Do you really want to remove this swimlane: "%s"?', $swimlane['name']) ?> - </p> +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this swimlane: "%s"?', $swimlane['name']) ?> + </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'SwimlaneController', 'remove', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> - </div> -</section> + <?= $this->modal->confirmButtons( + 'SwimlaneController', + 'remove', + array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']) + ) ?> +</div> diff --git a/app/Template/swimlane/table.php b/app/Template/swimlane/table.php index 81daed01..4c9390e0 100644 --- a/app/Template/swimlane/table.php +++ b/app/Template/swimlane/table.php @@ -20,8 +20,7 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'SwimlaneController', 'editDefault', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'editDefault', array('project_id' => $project['id'])) ?> </li> <li> <?php if ($default_swimlane['show_default_swimlane'] == 1): ?> @@ -58,8 +57,7 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?> </li> <li> <?php if ($swimlane['is_active']): ?> @@ -71,8 +69,7 @@ <?php endif ?> </li> <li> - <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?> </li> </ul> </div> diff --git a/app/Template/tag/create.php b/app/Template/tag/create.php index 9b32bc46..752a63e5 100644 --- a/app/Template/tag/create.php +++ b/app/Template/tag/create.php @@ -1,16 +1,12 @@ <div class="page-header"> <h2><?= t('Add new tag') ?></h2> </div> -<form method="post" class="popover-form" action="<?= $this->url->href('TagController', 'save') ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TagController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('project_id', $values) ?> <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/tag/edit.php b/app/Template/tag/edit.php index f751ff49..adef3568 100644 --- a/app/Template/tag/edit.php +++ b/app/Template/tag/edit.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('Edit a tag') ?></h2> </div> -<form method="post" class="popover-form" action="<?= $this->url->href('TagController', 'update', array('tag_id' => $tag['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TagController', 'update', array('tag_id' => $tag['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> @@ -9,9 +9,5 @@ <?= $this->form->label(t('Name'), 'name') ?> <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/tag/index.php b/app/Template/tag/index.php index 8e0c9a06..834e8e7c 100644 --- a/app/Template/tag/index.php +++ b/app/Template/tag/index.php @@ -2,8 +2,7 @@ <h2><?= t('Global tags') ?></h2> <ul> <li> - <i class="fa fa-plus" aria-hidden="true"></i> - <?= $this->url->link(t('Add new tag'), 'TagController', 'create', array(), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add new tag'), 'TagController', 'create') ?> </li> </ul> </div> @@ -20,10 +19,8 @@ <tr> <td><?= $this->text->e($tag['name']) ?></td> <td> - <i class="fa fa-times" aria-hidden="true"></i> - <?= $this->url->link(t('Remove'), 'TagController', 'confirm', array('tag_id' => $tag['id']), false, 'popover') ?> - <i class="fa fa-pencil-square-o" aria-hidden="true"></i> - <?= $this->url->link(t('Edit'), 'TagController', 'edit', array('tag_id' => $tag['id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'TagController', 'edit', array('tag_id' => $tag['id'])) ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TagController', 'confirm', array('tag_id' => $tag['id'])) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/tag/remove.php b/app/Template/tag/remove.php index 46ea3f99..47ba8d3d 100644 --- a/app/Template/tag/remove.php +++ b/app/Template/tag/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this tag: "%s"?', $tag['name']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TagController', 'remove', array('tag_id' => $tag['id']), true, 'btn btn-red popover-link') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TagController', + 'remove', + array('tag_id' => $tag['id']) + ) ?> </div> diff --git a/app/Template/task/details.php b/app/Template/task/details.php index 202991d2..b616afe3 100644 --- a/app/Template/task/details.php +++ b/app/Template/task/details.php @@ -168,7 +168,7 @@ <?php if ($editable && empty($task['date_started'])): ?> <div class="buttons-header"> - <?= $this->url->button('fa-play', t('Set start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?= $this->url->button('play', t('Set start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </div> <?php endif ?> diff --git a/app/Template/task/dropdown.php b/app/Template/task/dropdown.php index 86428059..bd424443 100644 --- a/app/Template/task/dropdown.php +++ b/app/Template/task/dropdown.php @@ -8,55 +8,43 @@ </li> <?php endif ?> <li> - <i class="fa fa-pencil-square-o fa-fw"></i> - <?= $this->url->link(t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-code-fork fa-fw"></i> - <?= $this->url->link(t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('code-fork', t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-external-link fa-fw"></i> - <?= $this->url->link(t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('external-link', t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-comment-o fa-fw"></i> - <?= $this->url->link(t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('comment-o', t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-camera fa-fw"></i> - <?= $this->url->link(t('Add a screenshot'), 'TaskPopoverController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('camera', t('Add a screenshot'), 'TaskPopoverController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-files-o fa-fw"></i> - <?= $this->url->link(t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('files-o', t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-clipboard fa-fw"></i> - <?= $this->url->link(t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('clipboard', t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-clone fa-fw"></i> - <?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php if ($this->projectRole->canRemoveTask($task)): ?> <li> - <i class="fa fa-trash-o fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> <?php if (isset($task['is_active']) && $this->projectRole->canChangeTaskStatusInColumn($task['project_id'], $task['column_id'])): ?> <li> <?php if ($task['is_active'] == 1): ?> - <i class="fa fa-times fa-fw"></i> - <?= $this->url->link(t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('times', t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php else: ?> - <i class="fa fa-check-square-o fa-fw"></i> - <?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('check-square-o', t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> <?php endif ?> </li> <?php endif ?> diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index 9472b6ba..9735efcb 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -35,72 +35,57 @@ </div> <ul> <li> - <i class="fa fa-pencil-square-o fa-fw"></i> - <?= $this->url->link(t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-refresh fa-rotate-90 fa-fw"></i> - <?= $this->url->link(t('Edit recurrence'), 'TaskRecurrenceController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('refresh fa-rotate-90', t('Edit recurrence'), 'TaskRecurrenceController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-code-fork fa-fw"></i> - <?= $this->url->link(t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('code-fork', t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-external-link fa-fw"></i> - <?= $this->url->link(t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('external-link', t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-comment-o fa-fw"></i> - <?= $this->url->link(t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('comment-o', t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-file fa-fw"></i> - <?= $this->url->link(t('Attach a document'), 'TaskFileController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('file', t('Attach a document'), 'TaskFileController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-camera fa-fw"></i> - <?= $this->url->link(t('Add a screenshot'), 'TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('camera', t('Add a screenshot'), 'TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-files-o fa-fw"></i> - <?= $this->url->link(t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('files-o', t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-clipboard fa-fw"></i> - <?= $this->url->link(t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('clipboard', t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-clone fa-fw"></i> - <?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php if ($task['is_active'] == 1 && $this->projectRole->isSortableColumn($task['project_id'], $task['column_id'])): ?> <li> - <i class="fa fa-arrows fa-fw"></i> - <?= $this->url->link(t('Move position'), 'TaskMovePositionController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->small('arrows', t('Move position'), 'TaskMovePositionController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> <?php if ($this->projectRole->canChangeTaskStatusInColumn($task['project_id'], $task['column_id'])): ?> <?php if ($task['is_active'] == 1): ?> <li> - <i class="fa fa-times fa-fw"></i> - <?= $this->url->link(t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('times', t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php else: ?> <li> - <i class="fa fa-check-square-o fa-fw"></i> - <?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('check-square-o', t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <?php endif ?> <?php endif ?> <?php if ($this->projectRole->canRemoveTask($task)): ?> <li> - <i class="fa fa-trash-o fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board')) ?> </li> <?php endif ?> diff --git a/app/Template/task_bulk/show.php b/app/Template/task_bulk/show.php index 11ddea31..2442779d 100644 --- a/app/Template/task_bulk/show.php +++ b/app/Template/task_bulk/show.php @@ -2,7 +2,7 @@ <h2><?= $this->text->e($project['name']) ?> > <?= t('Create tasks in bulk') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('TaskBulkController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TaskBulkController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('column_id', $values) ?> <?= $this->form->hidden('swimlane_id', $values) ?> @@ -16,9 +16,6 @@ <?= $this->form->textarea('tasks', $values, $errors, array('placeholder="'.t('My task title').'"')) ?> <p class="form-help"><?= t('Enter one task by line.') ?></p> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_creation/duplicate_projects.php b/app/Template/task_creation/duplicate_projects.php index dc0fa105..202b3ffe 100644 --- a/app/Template/task_creation/duplicate_projects.php +++ b/app/Template/task_creation/duplicate_projects.php @@ -5,10 +5,10 @@ <?php if (empty($projects_list)): ?> <p class="alert"><?= t('There is no destination project available.') ?></p> <div class="form-actions"> - <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $task['project_id']), false, 'close-popover btn') ?> + <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $task['project_id']), false, 'js-modal-close btn') ?> </div> <?php else: ?> - <form class="popover-form" method="post" action="<?= $this->url->href('TaskCreationController', 'duplicateProjects', array('project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('TaskCreationController', 'duplicateProjects', array('project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', $values) ?> @@ -20,9 +20,6 @@ array('multiple') ) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Duplicate') ?></button> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/task_creation/show.php b/app/Template/task_creation/show.php index 840d1804..56e4b398 100644 --- a/app/Template/task_creation/show.php +++ b/app/Template/task_creation/show.php @@ -1,50 +1,48 @@ <div class="page-header"> <h2><?= $this->text->e($project['name']) ?> > <?= t('New task') ?><?= $this->task->getNewTaskDropdown($project['id'], $values['swimlane_id'], $values['column_id']) ?></h2> </div> - -<form class="popover-form" method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> + <?= $this->form->hidden('project_id', $values) ?> - <div class="form-columns"> - <div class="form-column"> + <div class="task-form-container"> + <div class="task-form-main-column"> <?= $this->task->selectTitle($values, $errors) ?> <?= $this->task->selectDescription($values, $errors) ?> <?= $this->task->selectTags($project) ?> - + <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> - - <?php if (! isset($duplicate)): ?> - <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?> - <?= $this->form->checkbox('duplicate_multiple_projects', t('Duplicate to multiple projects'), 1) ?> - <?php endif ?> </div> - <div class="form-column"> - <?= $this->form->hidden('project_id', $values) ?> + <div class="task-form-secondary-column"> <?= $this->task->selectColor($values) ?> <?= $this->task->selectAssignee($users_list, $values, $errors) ?> <?= $this->task->selectCategory($categories_list, $values, $errors) ?> <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?> <?= $this->task->selectColumn($columns_list, $values, $errors) ?> <?= $this->task->selectPriority($project, $values) ?> - <?= $this->task->selectScore($values, $errors) ?> - <?= $this->task->selectReference($values, $errors) ?> <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?> </div> - <div class="form-column"> + <div class="task-form-secondary-column"> + <?= $this->task->selectDueDate($values, $errors) ?> + <?= $this->task->selectStartDate($values, $errors) ?> <?= $this->task->selectTimeEstimated($values, $errors) ?> <?= $this->task->selectTimeSpent($values, $errors) ?> - <?= $this->task->selectStartDate($values, $errors) ?> - <?= $this->task->selectDueDate($values, $errors) ?> + <?= $this->task->selectScore($values, $errors) ?> + <?= $this->task->selectReference($values, $errors) ?> <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?> </div> - </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="15"><?= t('Save') ?></button> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $values['project_id']), false, 'close-popover') ?> + <div class="task-form-bottom"> + <?php if (! isset($duplicate)): ?> + <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?> + <?= $this->form->checkbox('duplicate_multiple_projects', t('Duplicate to multiple projects'), 1) ?> + <?php endif ?> + + <?= $this->modal->submitButtons() ?> + </div> </div> </form> diff --git a/app/Template/task_duplication/copy.php b/app/Template/task_duplication/copy.php index 65cfe4d6..d96960fc 100644 --- a/app/Template/task_duplication/copy.php +++ b/app/Template/task_duplication/copy.php @@ -5,25 +5,25 @@ <?php if (empty($projects_list)): ?> <p class="alert"><?= t('There is no destination project available.') ?></p> <div class="form-actions"> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover btn') ?> + <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'js-modal-close btn') ?> </div> <?php else: ?> - <form class="popover-form" method="post" action="<?= $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> - + <form method="post" action="<?= $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->label(t('Project'), 'project_id') ?> - <?= $this->form->select( - 'project_id', - $projects_list, - $values, - array(), - array('data-redirect="'.$this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'), - 'task-reload-project-destination' - ) ?> - <span class="loading-icon" style="display: none"> <i class="fa fa-spinner fa-spin"></i></span> + <?= $this->app->component('select-dropdown-autocomplete', array( + 'name' => 'project_id', + 'items' => $projects_list, + 'defaultValue' => isset($values['project_id']) ? $values['project_id'] : null, + 'placeholder' => t('Choose a project'), + 'replace' => array( + 'regex' => 'PROJECT_ID', + 'url' => $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')), + ) + )) ?> <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> <?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?> @@ -41,10 +41,6 @@ <?= $this->form->select('owner_id', $users_list, $values) ?> <p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/task_duplication/duplicate.php b/app/Template/task_duplication/duplicate.php index c0baf94e..aa02b78c 100644 --- a/app/Template/task_duplication/duplicate.php +++ b/app/Template/task_duplication/duplicate.php @@ -7,9 +7,9 @@ <?= t('Do you really want to duplicate this task?') ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskDuplicationController', + 'duplicate', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes') + ) ?> </div> diff --git a/app/Template/task_duplication/move.php b/app/Template/task_duplication/move.php index 717d15e9..6c3e39ee 100644 --- a/app/Template/task_duplication/move.php +++ b/app/Template/task_duplication/move.php @@ -5,25 +5,26 @@ <?php if (empty($projects_list)): ?> <p class="alert"><?= t('There is no destination project available.') ?></p> <div class="form-actions"> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover btn') ?> + <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'js-modal-close btn') ?> </div> <?php else: ?> - <form class="popover-form" method="post" action="<?= $this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->label(t('Project'), 'project_id') ?> - <?= $this->form->select( - 'project_id', - $projects_list, - $values, - array(), - array('data-redirect="'.$this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'), - 'task-reload-project-destination' - ) ?> - <span class="loading-icon" style="display: none"> <i class="fa fa-spinner fa-spin"></i></span> + <?= $this->app->component('select-dropdown-autocomplete', array( + 'name' => 'project_id', + 'items' => $projects_list, + 'defaultValue' => isset($values['project_id']) ? $values['project_id'] : null, + 'placeholder' => t('Choose a project'), + 'replace' => array( + 'regex' => 'PROJECT_ID', + 'url' => $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')), + ) + )) ?> <?= $this->form->label(t('Swimlane'), 'swimlane_id') ?> <?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?> @@ -41,11 +42,7 @@ <?= $this->form->select('owner_id', $users_list, $values) ?> <p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/task_external_link/create.php b/app/Template/task_external_link/create.php index beddfc90..04d9bed0 100644 --- a/app/Template/task_external_link/create.php +++ b/app/Template/task_external_link/create.php @@ -2,12 +2,7 @@ <h2><?= t('Add a new external link') ?></h2> </div> -<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> +<form action="<?= $this->url->href('TaskExternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php index 917a28b9..df10d444 100644 --- a/app/Template/task_external_link/edit.php +++ b/app/Template/task_external_link/edit.php @@ -2,12 +2,7 @@ <h2><?= t('Edit external link') ?></h2> </div> -<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> +<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php index a88b29ce..a3665c0d 100644 --- a/app/Template/task_external_link/find.php +++ b/app/Template/task_external_link/find.php @@ -2,7 +2,7 @@ <h2><?= t('Add a new external link') ?></h2> </div> -<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> +<form action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> @@ -20,9 +20,5 @@ <?= $this->form->label(t('Link type'), 'type') ?> <?= $this->form->select('type', $types, $values) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Next') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_external_link/remove.php b/app/Template/task_external_link/remove.php index 2a888a60..ed8ad15f 100644 --- a/app/Template/task_external_link/remove.php +++ b/app/Template/task_external_link/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this link: "%s"?', $link['title']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskExternalLinkController', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskExternalLinkController', + 'remove', + array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']) + ) ?> </div> diff --git a/app/Template/task_external_link/table.php b/app/Template/task_external_link/table.php index a5fc7a33..aaa234bb 100644 --- a/app/Template/task_external_link/table.php +++ b/app/Template/task_external_link/table.php @@ -33,12 +33,10 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-edit fa-fw"></i> - <?= $this->url->link(t('Edit'), 'TaskExternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'TaskExternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-trash-o fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskExternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskExternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> </ul> </div> diff --git a/app/Template/task_file/create.php b/app/Template/task_file/create.php index e05cf829..eebb08eb 100644 --- a/app/Template/task_file/create.php +++ b/app/Template/task_file/create.php @@ -1,33 +1,20 @@ <div class="page-header"> <h2><?= t('Attach a document') ?></h2> </div> -<div id="file-done" style="display:none"> - <p class="alert alert-success"> - <?= t('All files have been uploaded successfully.') ?> - <?= $this->url->link(t('View uploaded files'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> - </p> -</div> - -<div id="file-error-max-size" style="display:none"> - <p class="alert alert-error"> - <?= t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) ?> - <a href="#" id="file-browser"><?= t('Choose files again') ?></a> - </p> -</div> -<div - id="file-dropzone" - data-max-size="<?= $max_size ?>" - data-url="<?= $this->url->href('TaskFileController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"> - <div id="file-dropzone-inner"> - <?= t('Drag and drop your files here') ?> <?= t('or') ?> <a href="#" id="file-browser"><?= t('choose files') ?></a> - </div> -</div> - -<input type="file" name="files[]" multiple style="display:none" id="file-form-element"> +<?= $this->app->component('file-upload', array( + 'maxSize' => $max_size, + 'url' => $this->url->to('TaskFileController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), + 'labelDropzone' => t('Drag and drop your files here'), + 'labelOr' => t('or'), + 'labelChooseFiles' => t('choose files'), + 'labelOversize' => t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)), + 'labelSuccess' => t('All files have been uploaded successfully.'), + 'labelCloseSuccess' => t('Close this window'), + 'labelUploadError' => t('Unable to upload this file.'), +)) ?> -<div class="form-actions"> - <input type="submit" value="<?= t('Upload files') ?>" class="btn btn-blue" id="file-upload-button" disabled> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> -</div> +<?= $this->modal->submitButtons(array( + 'submitLabel' => t('Upload files'), + 'disabled' => true, +)) ?> diff --git a/app/Template/task_file/files.php b/app/Template/task_file/files.php index 32bebdcb..ffebdb84 100644 --- a/app/Template/task_file/files.php +++ b/app/Template/task_file/files.php @@ -15,8 +15,7 @@ <ul> <?php if ($this->file->getPreviewType($file['name']) !== null): ?> <li> - <i class="fa fa-eye fa-fw"></i> - <?= $this->url->link(t('View file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->large('eye', t('View file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?> </li> <?php elseif ($this->file->getBrowserViewType($file['name']) !== null): ?> <li> @@ -30,8 +29,7 @@ </li> <?php if ($this->user->hasProjectAccess('TaskFileController', 'remove', $task['project_id'])): ?> <li> - <i class="fa fa-trash fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/task_file/images.php b/app/Template/task_file/images.php index 8be89274..e3599441 100644 --- a/app/Template/task_file/images.php +++ b/app/Template/task_file/images.php @@ -24,8 +24,7 @@ </li> <?php if ($this->user->hasProjectAccess('TaskFileController', 'remove', $task['project_id'])): ?> <li> - <i class="fa fa-trash fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/task_file/remove.php b/app/Template/task_file/remove.php index 42894f05..e7ffe560 100644 --- a/app/Template/task_file/remove.php +++ b/app/Template/task_file/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this file: "%s"?', $this->text->e($file['name'])) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskFileController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskFileController', + 'remove', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']) + ) ?> </div> diff --git a/app/Template/task_file/screenshot.php b/app/Template/task_file/screenshot.php index 6300159f..dad8c233 100644 --- a/app/Template/task_file/screenshot.php +++ b/app/Template/task_file/screenshot.php @@ -6,14 +6,10 @@ <p id="screenshot-inner"><?= t('Take a screenshot and press CTRL+V or ⌘+V to paste here.') ?></p> </div> -<form class="popover-form" action="<?= $this->url->href('TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post"> - <input type="hidden" name="screenshot"/> +<form action="<?= $this->url->href('TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post"> <?= $this->form->csrf() ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->app->component('screenshot') ?> + <?= $this->modal->submitButtons() ?> </form> <p class="alert alert-info"><?= t('This feature does not work with all browsers.') ?></p> diff --git a/app/Template/task_gantt/show.php b/app/Template/task_gantt/show.php index c5d338fb..ddc70a43 100644 --- a/app/Template/task_gantt/show.php +++ b/app/Template/task_gantt/show.php @@ -11,8 +11,7 @@ <?= $this->url->link(t('Sort by date'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'date')) ?> </li> <li> - <i class="fa fa-plus fa-fw"></i> - <?= $this->url->link(t('Add task'), 'TaskGanttCreationController', 'show', array('project_id' => $project['id']), false, 'popover') ?> + <?= $this->modal->large('plus', t('Add task'), 'TaskCreationController', 'show', array('project_id' => $project['id'])) ?> </li> </ul> </div> diff --git a/app/Template/task_gantt_creation/show.php b/app/Template/task_gantt_creation/show.php deleted file mode 100644 index 7906c39a..00000000 --- a/app/Template/task_gantt_creation/show.php +++ /dev/null @@ -1,46 +0,0 @@ -<div class="page-header"> - <h2><?= $this->text->e($project['name']) ?> > <?= t('New task') ?></h2> -</div> -<form class="popover-form" method="post" action="<?= $this->url->href('TaskGanttCreationController', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off"> - <?= $this->form->csrf() ?> - <?= $this->form->hidden('project_id', $values) ?> - <?= $this->form->hidden('column_id', $values) ?> - <?= $this->form->hidden('position', $values) ?> - - <div class="form-columns"> - <div class="form-column"> - <?= $this->task->selectTitle($values, $errors) ?> - <?= $this->task->selectDescription($values, $errors) ?> - <?= $this->task->selectTags($project) ?> - - <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> - </div> - - <div class="form-column"> - <?= $this->task->selectColor($values) ?> - <?= $this->task->selectAssignee($users_list, $values, $errors) ?> - <?= $this->task->selectCategory($categories_list, $values, $errors) ?> - <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?> - <?= $this->task->selectPriority($project, $values) ?> - <?= $this->task->selectScore($values, $errors) ?> - <?= $this->task->selectReference($values, $errors) ?> - - <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?> - </div> - - <div class="form-column"> - <?= $this->task->selectTimeEstimated($values, $errors) ?> - <?= $this->task->selectTimeSpent($values, $errors) ?> - <?= $this->task->selectStartDate($values, $errors) ?> - <?= $this->task->selectDueDate($values, $errors) ?> - - <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?> - </div> - </div> - - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="15"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskGanttController', 'show', array('project_id' => $values['project_id']), false, 'close-popover') ?> - </div> -</form> diff --git a/app/Template/task_internal_link/create.php b/app/Template/task_internal_link/create.php index fed29605..3c39b87c 100644 --- a/app/Template/task_internal_link/create.php +++ b/app/Template/task_internal_link/create.php @@ -2,7 +2,7 @@ <h2><?= t('Add a new link') ?></h2> </div> -<form class="popover-form" action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> +<form action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?> @@ -25,9 +25,5 @@ ), 'autocomplete') ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_internal_link/edit.php b/app/Template/task_internal_link/edit.php index f4df57bd..5abf7b65 100644 --- a/app/Template/task_internal_link/edit.php +++ b/app/Template/task_internal_link/edit.php @@ -26,9 +26,5 @@ ), 'autocomplete') ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_internal_link/remove.php b/app/Template/task_internal_link/remove.php index 966ad116..f8d91a81 100644 --- a/app/Template/task_internal_link/remove.php +++ b/app/Template/task_internal_link/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this link with task #%d?', $link['opposite_task_id']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskInternalLinkController', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskInternalLinkController', + 'remove', + array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']) + ) ?> </div> diff --git a/app/Template/task_internal_link/table.php b/app/Template/task_internal_link/table.php index 3a3a2b88..255ecc97 100644 --- a/app/Template/task_internal_link/table.php +++ b/app/Template/task_internal_link/table.php @@ -73,12 +73,10 @@ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> <ul> <li> - <i class="fa fa-edit fa-fw"></i> - <?= $this->url->link(t('Edit'), 'TaskInternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->medium('edit', t('Edit'), 'TaskInternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> - <i class="fa fa-trash-o fa-fw"></i> - <?= $this->url->link(t('Remove'), 'TaskInternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskInternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> </ul> </div> diff --git a/app/Template/task_modification/show.php b/app/Template/task_modification/show.php index 734b247a..23a0a43b 100644 --- a/app/Template/task_modification/show.php +++ b/app/Template/task_modification/show.php @@ -1,13 +1,13 @@ <div class="page-header"> <h2><?= $this->text->e($project['name']) ?> > <?= $this->text->e($task['title']) ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('id', $values) ?> <?= $this->form->hidden('project_id', $values) ?> - <div class="form-columns"> - <div class="form-column"> + <div class="task-form-container"> + <div class="task-form-main-column"> <?= $this->task->selectTitle($values, $errors) ?> <?= $this->task->selectDescription($values, $errors) ?> <?= $this->task->selectTags($project, $tags) ?> @@ -15,30 +15,28 @@ <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> </div> - <div class="form-column"> + <div class="task-form-secondary-column"> <?= $this->task->selectColor($values) ?> <?= $this->task->selectAssignee($users_list, $values, $errors) ?> <?= $this->task->selectCategory($categories_list, $values, $errors) ?> <?= $this->task->selectPriority($project, $values) ?> - <?= $this->task->selectScore($values, $errors) ?> - <?= $this->task->selectReference($values, $errors) ?> <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?> </div> - <div class="form-column"> + <div class="task-form-secondary-column"> + <?= $this->task->selectDueDate($values, $errors) ?> + <?= $this->task->selectStartDate($values, $errors) ?> <?= $this->task->selectTimeEstimated($values, $errors) ?> <?= $this->task->selectTimeSpent($values, $errors) ?> - <?= $this->task->selectStartDate($values, $errors) ?> - <?= $this->task->selectDueDate($values, $errors) ?> + <?= $this->task->selectScore($values, $errors) ?> + <?= $this->task->selectReference($values, $errors) ?> <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?> </div> - </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue" tabindex="15"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> + <div class="task-form-bottom"> + <?= $this->modal->submitButtons() ?> + </div> </div> </form> diff --git a/app/Template/task_move_position/show.php b/app/Template/task_move_position/show.php index 91241016..a73be785 100644 --- a/app/Template/task_move_position/show.php +++ b/app/Template/task_move_position/show.php @@ -14,10 +14,6 @@ 'afterLabel' => t('Insert after this task'), )) ?> -<?= $this->app->component('submit-cancel', array( - 'submitLabel' => t('Save'), - 'orLabel' => t('or'), - 'cancelLabel' => t('cancel'), -)) ?> +<?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/task_recurrence/edit.php b/app/Template/task_recurrence/edit.php index 09d14826..8e0b936d 100644 --- a/app/Template/task_recurrence/edit.php +++ b/app/Template/task_recurrence/edit.php @@ -15,7 +15,7 @@ <?php if ($task['recurrence_status'] != \Kanboard\Model\TaskModel::RECURRING_STATUS_PROCESSED): ?> - <form class="popover-form" method="post" action="<?= $this->url->href('TaskRecurrenceController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> + <form method="post" action="<?= $this->url->href('TaskRecurrenceController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off"> <?= $this->form->csrf() ?> @@ -37,11 +37,7 @@ <?= $this->form->label(t('Base date to calculate new due date'), 'recurrence_basedate') ?> <?= $this->form->select('recurrence_basedate', $recurrence_basedate_list, $values, $errors) ?> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <?php endif ?> diff --git a/app/Template/task_status/close.php b/app/Template/task_status/close.php index 2d7b0ce5..0bf3c8e3 100644 --- a/app/Template/task_status/close.php +++ b/app/Template/task_status/close.php @@ -7,9 +7,9 @@ <?= t('Do you really want to close the task "%s" as well as all subtasks?', $task['title']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskStatusController', + 'close', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes') + ) ?> </div> diff --git a/app/Template/task_status/open.php b/app/Template/task_status/open.php index 242b5db5..42765e34 100644 --- a/app/Template/task_status/open.php +++ b/app/Template/task_status/open.php @@ -7,9 +7,9 @@ <?= t('Do you really want to open this task: "%s"?', $task['title']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskStatusController', + 'open', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes') + ) ?> </div> diff --git a/app/Template/task_suppression/remove.php b/app/Template/task_suppression/remove.php index 5d0f7720..4b0666d8 100644 --- a/app/Template/task_suppression/remove.php +++ b/app/Template/task_suppression/remove.php @@ -7,9 +7,9 @@ <?= t('Do you really want to remove this task: "%s"?', $this->text->e($task['title'])) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TaskSuppressionController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => $redirect), true, 'btn btn-red popover-link') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'TaskSuppressionController', + 'remove', + array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => $redirect) + ) ?> </div> diff --git a/app/Template/twofactor/disable.php b/app/Template/twofactor/disable.php index bc419181..c44c450f 100644 --- a/app/Template/twofactor/disable.php +++ b/app/Template/twofactor/disable.php @@ -7,8 +7,9 @@ <?= t('Do you really want to disable the two factor authentication for this user: "%s"?', $user['name'] ?: $user['username']) ?> </p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'TwoFactorController', 'disable', array('user_id' => $user['id'], 'disable' => 'yes'), true, 'btn btn-red') ?> - <?= t('or') ?> <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?> - </div> + <?= $this->modal->confirmButtons( + 'TwoFactorController', + 'disable', + array('user_id' => $user['id'], 'disable' => 'yes') + ) ?> </div> diff --git a/app/Template/user_creation/local.php b/app/Template/user_creation/local.php index 059a0114..08784925 100644 --- a/app/Template/user_creation/local.php +++ b/app/Template/user_creation/local.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('New local user') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <div class="form-columns"> @@ -39,9 +39,5 @@ </div> </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> diff --git a/app/Template/user_creation/remote.php b/app/Template/user_creation/remote.php index 41d0d3c7..0f28c1c4 100644 --- a/app/Template/user_creation/remote.php +++ b/app/Template/user_creation/remote.php @@ -1,7 +1,7 @@ <div class="page-header"> <h2><?= t('New remote user') ?></h2> </div> -<form class="popover-form" method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off"> +<form method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off"> <?= $this->form->csrf() ?> <?= $this->form->hidden('is_ldap_user', array('is_ldap_user' => 1)) ?> @@ -37,11 +37,7 @@ </div> </div> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons() ?> </form> <div class="alert alert-info"> <ul> diff --git a/app/Template/user_import/show.php b/app/Template/user_import/show.php index 663f107e..fb593bb6 100644 --- a/app/Template/user_import/show.php +++ b/app/Template/user_import/show.php @@ -33,9 +33,5 @@ <p class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? $this->text->bytes($max_size) : $max_size ?></p> - <div class="form-actions"> - <button type="submit" class="btn btn-blue"><?= t('Import') ?></button> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->submitButtons(array('labelSubmit' => t('Import'))) ?> </form> diff --git a/app/Template/user_list/dropdown.php b/app/Template/user_list/dropdown.php index 9e90c230..44a78162 100644 --- a/app/Template/user_list/dropdown.php +++ b/app/Template/user_list/dropdown.php @@ -7,20 +7,17 @@ </li> <?php if ($user['is_active'] == 1 && $this->user->hasAccess('UserStatusController', 'disable') && ! $this->user->isCurrentUser($user['id'])): ?> <li> - <i class="fa fa-times fa-fw"></i> - <?= $this->url->link(t('Disable'), 'UserStatusController', 'confirmDisable', array('user_id' => $user['id']), false, 'popover') ?> + <?= $this->modal->confirm('times', t('Disable'), 'UserStatusController', 'confirmDisable', array('user_id' => $user['id'])) ?> </li> <?php endif ?> <?php if ($user['is_active'] == 0 && $this->user->hasAccess('UserStatusController', 'enable') && ! $this->user->isCurrentUser($user['id'])): ?> <li> - <i class="fa fa-check-square-o fa-fw"></i> - <?= $this->url->link(t('Enable'), 'UserStatusController', 'confirmEnable', array('user_id' => $user['id']), false, 'popover') ?> + <?= $this->modal->confirm('check-square-o', t('Enable'), 'UserStatusController', 'confirmEnable', array('user_id' => $user['id'])) ?> </li> <?php endif ?> <?php if ($this->user->hasAccess('UserStatusController', 'remove') && ! $this->user->isCurrentUser($user['id'])): ?> <li> - <i class="fa fa-trash-o fa-fw"></i> - <?= $this->url->link(t('Remove'), 'UserStatusController', 'confirmRemove', array('user_id' => $user['id']), false, 'popover') ?> + <?= $this->modal->confirm('trash-o', t('Remove'), 'UserStatusController', 'confirmRemove', array('user_id' => $user['id'])) ?> </li> <?php endif ?> </ul> diff --git a/app/Template/user_list/show.php b/app/Template/user_list/show.php index 5e285c89..92ea74e7 100644 --- a/app/Template/user_list/show.php +++ b/app/Template/user_list/show.php @@ -2,10 +2,18 @@ <div class="page-header"> <?php if ($this->user->hasAccess('UserCreationController', 'show')): ?> <ul> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'UserCreationController', 'show', array(), false, 'popover') ?></li> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'UserCreationController', 'show', array('remote' => 1), false, 'popover') ?></li> - <li><i class="fa fa-upload fa-fw"></i><?= $this->url->link(t('Import'), 'UserImportController', 'show', array(), false, 'popover') ?></li> - <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li> + <li> + <?= $this->modal->medium('plus', t('New local user'), 'UserCreationController', 'show') ?> + </li> + <li> + <?= $this->modal->medium('plus', t('New remote user'), 'UserCreationController', 'show', array('remote' => 1)) ?> + </li> + <li> + <?= $this->modal->medium('upload', t('Import'), 'UserImportController', 'show') ?> + </li> + <li> + <i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?> + </li> </ul> <?php endif ?> </div> diff --git a/app/Template/user_status/disable.php b/app/Template/user_status/disable.php index d30b0c20..1309b080 100644 --- a/app/Template/user_status/disable.php +++ b/app/Template/user_status/disable.php @@ -5,9 +5,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to disable this user: "%s"?', $user['name'] ?: $user['username']) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'UserStatusController', 'disable', array('user_id' => $user['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'UserStatusController', + 'disable', + array('user_id' => $user['id']) + ) ?> </div> diff --git a/app/Template/user_status/enable.php b/app/Template/user_status/enable.php index 29d25eee..2413739e 100644 --- a/app/Template/user_status/enable.php +++ b/app/Template/user_status/enable.php @@ -5,9 +5,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to enable this user: "%s"?', $user['name'] ?: $user['username']) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'UserStatusController', 'enable', array('user_id' => $user['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'UserStatusController', + 'enable', + array('user_id' => $user['id']) + ) ?> </div> diff --git a/app/Template/user_status/remove.php b/app/Template/user_status/remove.php index 2b8f2df5..6cd3f63a 100644 --- a/app/Template/user_status/remove.php +++ b/app/Template/user_status/remove.php @@ -5,9 +5,9 @@ <div class="confirm"> <p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['name'] ?: $user['username']) ?></p> - <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'UserStatusController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red') ?> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?> - </div> + <?= $this->modal->confirmButtons( + 'UserStatusController', + 'remove', + array('user_id' => $user['id']) + ) ?> </div> diff --git a/app/Template/user_view/layout.php b/app/Template/user_view/layout.php index c3604b99..4f0eca94 100644 --- a/app/Template/user_view/layout.php +++ b/app/Template/user_view/layout.php @@ -2,11 +2,21 @@ <div class="page-header"> <?php if ($this->user->hasAccess('UserCreationController', 'show')): ?> <ul> - <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?></li> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'UserCreationController', 'show', array(), false, 'popover') ?></li> - <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'UserCreationController', 'show', array('remote' => 1), false, 'popover') ?></li> - <li><i class="fa fa-upload fa-fw"></i><?= $this->url->link(t('Import'), 'UserImportController', 'show', array(), false, 'popover') ?></li> - <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li> + <li> + <i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?> + </li> + <li> + <?= $this->modal->medium('plus', t('New local user'), 'UserCreationController', 'show') ?> + </li> + <li> + <?= $this->modal->medium('plus', t('New remote user'), 'UserCreationController', 'show', array('remote' => 1)) ?> + </li> + <li> + <?= $this->modal->medium('upload', t('Import'), 'UserImportController', 'show') ?> + </li> + <li> + <i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?> + </li> </ul> <?php endif ?> </div> diff --git a/app/Validator/ProjectValidator.php b/app/Validator/ProjectValidator.php index 8c6117a4..5b10026c 100644 --- a/app/Validator/ProjectValidator.php +++ b/app/Validator/ProjectValidator.php @@ -28,7 +28,6 @@ class ProjectValidator extends BaseValidator new Validators\Integer('priority_start', t('This value must be an integer')), new Validators\Integer('priority_end', t('This value must be an integer')), new Validators\Integer('is_active', t('This value must be an integer')), - new Validators\NotEmpty('name', t('This field cannot be empty')), new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50), new Validators\MaxLength('identifier', t('The maximum length is %d characters', 50), 50), new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10), @@ -47,15 +46,15 @@ class ProjectValidator extends BaseValidator */ public function validateCreation(array $values) { - $rules = array( - new Validators\Required('name', t('The project name is required')), - ); - if (! empty($values['identifier'])) { $values['identifier'] = strtoupper($values['identifier']); } - $v = new Validator($values, array_merge($this->commonValidationRules(), $rules)); + $rules = array( + new Validators\Required('name', t('The project name is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); return array( $v->execute(), @@ -77,6 +76,7 @@ class ProjectValidator extends BaseValidator } $rules = array( + new Validators\NotEmpty('name', t('This field cannot be empty')), new Validators\Required('id', t('This value is required')), ); diff --git a/assets/css/app.min.css b/assets/css/app.min.css index 9db0c94f..433b7101 100644 --- a/assets/css/app.min.css +++ b/assets/css/app.min.css @@ -1 +1 @@ -h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{margin-left:10px;margin-right:10px;padding-bottom:10px;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}small{font-size:0.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.pull-right{text-align:right}ul.no-bullet li{list-style-type:none;margin-left:0}.chosen-select{min-height:27px}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:#36c;border:none}a:focus{outline:0;color:#DF5353;text-decoration:none}a:hover{color:#333;text-decoration:none}h1,h2,h3{font-weight:normal;color:#333}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:0.8em}table.table-striped tr:nth-child(odd){background:#fefefe}@media (max-width: 768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:0.5em 3px;border:1px solid #eee;background:#fbfbfb}table th a{text-decoration:none;color:#333}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid #eee;padding:0.5em 3px;vertical-align:top}table td li{margin-left:20px}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:#dedede}.draggable-row-handle:hover{color:#333}tr.draggable-item-selected{background:#fff;border:2px solid #666;box-shadow:4px 2px 10px -4px rgba(0,0,0,0.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:#FEFFF2}form{margin-bottom:20px}label{cursor:pointer;display:block;margin-top:10px}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:#999;border:1px solid #ccc;width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;font-family:sans-serif;margin-top:10px;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::-webkit-input-placeholder,input[type="date"]::-webkit-input-placeholder,input[type="email"]::-webkit-input-placeholder,input[type="password"]::-webkit-input-placeholder,input[type="text"]:not(.input-addon-field)::-webkit-input-placeholder{color:#dedede}input[type="number"]::-moz-placeholder,input[type="date"]::-moz-placeholder,input[type="email"]::-moz-placeholder,input[type="password"]::-moz-placeholder,input[type="text"]:not(.input-addon-field)::-moz-placeholder{color:#dedede}input[type="number"]:-ms-input-placeholder,input[type="date"]:-ms-input-placeholder,input[type="email"]:-ms-input-placeholder,input[type="password"]:-ms-input-placeholder,input[type="text"]:not(.input-addon-field):-ms-input-placeholder{color:#dedede}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}textarea{padding:3px;border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-family:sans-serif;font-size:1em}textarea::-webkit-input-placeholder{color:#dedede}textarea::-moz-placeholder{color:#dedede}textarea:-ms-input-placeholder{color:#dedede}select{font-size:1.0em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:bold}@media (max-width: 480px){.form-required{display:none}}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:0.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-login{max-width:350px;margin:8% auto 0}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:bold}.reset-password{margin-top:20px}.reset-password a{color:#999}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:#999;margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:rgba(147,128,108,0.1);color:#666;font:inherit;font-weight:normal}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width: 480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,0.25);padding:4px 0.75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:normal;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid #ddd;background:#f5f5f5;color:#333}.btn:hover,.btn:focus{border-color:#bbb;background:#fafafa;color:#000}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}.btn-red:hover,.btn-red:focus{border-color:#b0281a;background:#c53727;color:#fff}.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}.btn-blue:hover,.btn-blue:focus{border-color:#3079ed;background:#357ae8;color:#fff}.btn:disabled{color:#ccc;border-color:#ccc;background:#f7f7f7}.buttons-header{font-size:0.8em;margin-top:5px;margin-bottom:15px}.tooltip-arrow:after{background:#fff;border:1px solid #aaaaaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:600px}.ui-tooltip-content .markdown p{margin-bottom:0}.ui-tooltip li{list-style-type:none}.tooltip .fa-info-circle{color:#999}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:0.9em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:#333;text-decoration:none}.dropdown-menu-link-text:hover{text-decoration:underline}.accordion-title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEQ5RDgxQzc2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEQ5RDgxQzg2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0RDlEODFDNTZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0RDlEODFDNjZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvXFWFAAAAAYSURBVHjaYvj//z8D0/Pnz/8zgFgAAQYAS5UJscReGMIAAAAASUVORK5CYII=) repeat-x scroll 0 10px}.accordion-title h3{display:inline;padding-right:5px;background:#fff}.accordion-content{margin-top:15px;margin-bottom:25px}.accordion-toggle{color:#333;text-decoration:none}.accordion-toggle:focus{color:#333}.accordion-toggle:hover{color:#999}.accordion-toggle:before{content:"\f0d7"}.accordion-collapsed{margin-bottom:25px}.accordion-collapsed .accordion-toggle:before{content:"\f0da"}.accordion-collapsed .accordion-content{display:none}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:#555;cursor:pointer;border-bottom:1px solid #f8f8f8;line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid #ccc;border-radius:5px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:#555;position:absolute;top:4px;right:5px;cursor:pointer}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:#333;font-weight:bold;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:#999;font-weight:normal}#main .confirm{max-width:700px}#popover-container{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);overflow:auto;z-index:100}#popover-content{position:fixed;width:950px;max-width:95%;max-height:calc(100% - 50px);top:5%;left:50%;transform:translateX(-50%);padding:0 15px 15px;background:#fff;overflow:auto}#popover-content-header{text-align:right}#popover-close-button{color:#333}#popover-close-button:hover{color:#b94a48}.popover-form{margin-bottom:0}.pagination{text-align:center}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-top:5px;margin-bottom:5px;border-bottom:1px solid #dedede}header>*{box-sizing:border-box}header>*{width:1%}header .menus-container{width:10%}@media (min-width: 768px) and (max-width: 1150px){header .menus-container{width:15%}}@media (max-width: 768px){header .menus-container{width:30%;order:2}}header .board-selector-container{width:25%}@media (min-width: 768px) and (max-width: 1150px){header .board-selector-container{width:20%}}@media (max-width: 768px){header .board-selector-container{width:70%;order:1;margin-bottom:5px}}header .title-container{width:65%}@media (max-width: 768px){header .title-container{width:100%;order:3}}header h1{font-size:1.5em}header h1 .tooltip{opacity:0.3;font-size:0.7em}.web-notification-icon{color:#36c}.web-notification-icon:focus,.web-notification-icon:hover{color:#000}.logo a{opacity:0.5;color:#d40000;text-decoration:none}.logo span{color:#333}.logo a:hover{opacity:0.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#999}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width: 480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:bold;color:#000;text-decoration:none}.sidebar-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.sidebar-container>*{box-sizing:border-box}.sidebar-container>*{width:1%}.sidebar-content{padding-left:10px;width:82%}@media (max-width: 480px){.sidebar-content{width:100%}}.sidebar{max-width:240px;min-width:190px;width:18%}@media (max-width: 480px){.sidebar{width:100%;max-width:99%;min-width:0}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:#999;font-weight:300}.sidebar>ul a:hover{color:#333}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted #efefef;padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:#333;font-weight:bold}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:#555}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:#fff;text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li.file-error{font-weight:bold;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,0.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:0.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:0.9em;color:#555}.file-thumbnail-description{font-size:0.8em;color:#999;margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:180px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:800px}.action-menu{color:#333;text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width: 480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width: 480px){.project-overview-column{text-align:left}}.project-overview-column small{color:#999}.project-overview-column strong{color:#555;display:block}@media (max-width: 480px){.project-overview-column strong{display:inline}}.project-header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-bottom:8px}.project-header>*{box-sizing:border-box}.project-header>*{width:1%}.project-header .dropdown-component{margin-top:4px;width:5%}@media (min-width: 768px) and (max-width: 1150px){.project-header .dropdown-component{width:8%}}@media (max-width: 768px){.project-header .dropdown-component{width:100%}}.project-header .views-switcher-component{margin-top:4px;width:38%}@media (max-width: 1300px){.project-header .views-switcher-component{width:45%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .views-switcher-component{width:92%}}@media (max-width: 768px){.project-header .views-switcher-component{margin-top:0;width:100%}}.project-header .filter-box-component{margin:0;width:55%}@media (max-width: 1300px){.project-header .filter-box-component{width:50%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}@media (max-width: 768px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}.project-header .filter-box-component form{margin:0}.views{display:inline-block;margin-right:10px;font-size:0.9em}@media (max-width: 560px){.views{width:100%}}@media (max-width: 768px){.views{margin-top:10px;font-size:1em}}@media (max-width: 480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:#fafafa;border:1px solid #ddd;border-right:none;padding:4px 8px;display:inline}@media (max-width: 560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid #ddd}}.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid #ddd;border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:#555;text-decoration:none}.views a:hover{color:#333;text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:#999}.dashboard-table-link{font-weight:bold;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast: active), (-ms-high-contrast: none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon a{text-decoration:none;color:#36c;font-size:1.6em;line-height:70%}.board-add-icon a:focus,.board-add-icon a:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-limit{background-color:#DF5353}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:0.9em}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board .dropdown-menu{font-weight:bold}.task-board .task-score{font-weight:bold}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:0.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{font-weight:500;color:#000;border:1px solid #555;padding:1px 2px 1px 2px;border-radius:4px}.task-board-category:hover{opacity:0.6}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:0.6}.task-board-icons{font-size:0.8em;text-align:right;margin-top:4px;margin-bottom:2px}.task-board-icons a{opacity:0.5}.task-board-icons span{opacity:0.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.flag-milestone{color:green}.task-board-age{display:inline-block}span.task-board-age-total{border:#666 1px solid;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-board-age-column{border:#666 1px solid;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board-date{font-weight:bold;color:#000}span.task-board-date-today{opacity:1.0;color:#36c}span.task-board-date-overdue{opacity:1.0;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:#333;border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}#task-summary{margin-bottom:15px}#task-summary h2{color:#555;font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:15px}.task-summary-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}@media (max-width: 480px){.task-summary-columns{display:block}}.task-summary-column{color:#333}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-table .dropdown-menu{color:#000;text-decoration:none;font-weight:bold}.task-table .dropdown-menu:focus,.task-table .dropdown-menu:hover{text-decoration:underline}td.task-table a{color:#000;text-decoration:none}td.task-table a:hover{text-decoration:underline}.comment-sorting{text-align:right}.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}.comment-sorting a:hover{color:#999}.comment{padding:5px;margin-bottom:15px}.comment:hover{background:#fafafa}.comment-title{border-bottom:1px dotted #eee;margin-left:55px;margin-bottom:10px}.comment-date{color:#999;font-weight:200}.comment-actions{font-size:0.8em;margin-left:55px;margin-top:8px}.comment-actions li{display:inline}.comment-actions a{color:#999;text-decoration:none}.comment-actions a:focus,.comment-actions a:hover{color:#333;text-decoration:underline}.comment-content{margin-left:55px}.subtasks-table td{vertical-align:middle}.task-links-table td{vertical-align:middle}.task-links-task-count{color:#999}.task-link-closed{text-decoration:line-through}.text-editor a{font-size:1em;color:#999;text-decoration:none;margin-right:10px}.text-editor a:hover{color:#36c}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:400px;height:200px;overflow:auto;padding:2px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:bold}.markdown h2{font-weight:bold}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#555}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;margin-bottom:30px}.documentation h2{text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.listing{border-radius:4px;padding:8px 35px 8px 14px;margin-bottom:20px;border:1px solid #ddd;color:#333;background-color:#fcfcfc;overflow:auto}.listing li{list-style-type:square;margin-left:20px;margin-bottom:3px}.listing ul{margin-top:15px;margin-bottom:15px}.activity-event{margin-bottom:15px;padding:10px}.activity-event:hover{background:#fafafa}.activity-date{margin-left:10px;font-weight:normal;color:#999}.activity-content{margin-left:55px}.activity-title{font-weight:bold;color:#000;border-bottom:1px dotted #efefef}.activity-description{color:#555;margin-top:10px}@media (max-width: 480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}div.ganttview-hzheader-month,div.ganttview-hzheader-day,div.ganttview-vtheader,div.ganttview-vtheader-item-name,div.ganttview-vtheader-series,div.ganttview-grid,div.ganttview-grid-row-cell{float:left}div.ganttview-hzheader-month,div.ganttview-hzheader-day{text-align:center}div.ganttview-grid-row-cell.last,div.ganttview-hzheader-day.last,div.ganttview-hzheader-month.last{border-right:none}div.ganttview{border:1px solid #999}div.ganttview-hzheader-month{width:60px;height:20px;border-right:1px solid #d0d0d0;line-height:20px;overflow:hidden}div.ganttview-hzheader-day{width:20px;height:20px;border-right:1px solid #f0f0f0;border-top:1px solid #d0d0d0;line-height:20px;color:#555}div.ganttview-vtheader{margin-top:41px;width:400px;overflow:hidden;background-color:#fff}div.ganttview-vtheader-item{color:#555}div.ganttview-vtheader-series-name{width:400px;height:31px;line-height:31px;padding-left:3px;border-top:1px solid #d0d0d0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.ganttview-vtheader-series-name a{color:#555;text-decoration:none}div.ganttview-vtheader-series-name a:hover{color:#333;text-decoration:underline}div.ganttview-vtheader-series-name a i{color:#000}div.ganttview-vtheader-series-name a:hover i{color:#555}div.ganttview-slide-container{overflow:auto;border-left:1px solid #999}div.ganttview-grid-row-cell{width:20px;height:31px;border-right:1px solid #f0f0f0;border-top:1px solid #f0f0f0}div.ganttview-grid-row-cell.ganttview-weekend{background-color:#fafafa}div.ganttview-blocks{margin-top:40px}div.ganttview-block-container{height:28px;padding-top:4px}div.ganttview-block{position:relative;height:25px;background-color:#E5ECF9;border:1px solid #c0c0c0;border-radius:3px}.ganttview-block-movable{cursor:move}div.ganttview-block-not-defined{border-color:#000;background-color:#000}div.ganttview-block-text{position:absolute;height:12px;font-size:0.7em;color:#999;padding:2px 3px}div.ganttview-block div.ui-resizable-handle.ui-resizable-s{bottom:-0}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:0.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:0.6}.slideshow-icon:hover{opacity:0.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em} +h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0;font-size:100%}body{margin-left:10px;margin-right:10px;padding-bottom:10px;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility}small{font-size:0.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,0.1);border-bottom:1px solid rgba(255,255,255,0.3)}.pull-right{text-align:right}ul.no-bullet li{list-style-type:none;margin-left:0}.chosen-select{min-height:27px}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:#36c;border:none}a:focus{outline:0;color:#DF5353;text-decoration:none}a:hover{color:#333;text-decoration:none}a .fa{text-decoration:none;color:#333}h1,h2,h3{font-weight:normal;color:#333}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:0.8em}table.table-striped tr:nth-child(odd){background:#fefefe}@media (max-width: 768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:0.5em 3px;border:1px solid #eee;background:#fbfbfb}table th a{text-decoration:none;color:#333}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid #eee;padding:0.5em 3px;vertical-align:top}table td li{margin-left:20px}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:#dedede}.draggable-row-handle:hover{color:#333}tr.draggable-item-selected{background:#fff;border:2px solid #666;box-shadow:4px 2px 10px -4px rgba(0,0,0,0.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:#FEFFF2}fieldset{border:1px solid #ccc;margin-top:20px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:#999;border:1px solid #ccc;width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;font-family:sans-serif;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::-webkit-input-placeholder,input[type="date"]::-webkit-input-placeholder,input[type="email"]::-webkit-input-placeholder,input[type="password"]::-webkit-input-placeholder,input[type="text"]:not(.input-addon-field)::-webkit-input-placeholder{color:#dedede}input[type="number"]::-moz-placeholder,input[type="date"]::-moz-placeholder,input[type="email"]::-moz-placeholder,input[type="password"]::-moz-placeholder,input[type="text"]:not(.input-addon-field)::-moz-placeholder{color:#dedede}input[type="number"]:-ms-input-placeholder,input[type="date"]:-ms-input-placeholder,input[type="email"]:-ms-input-placeholder,input[type="password"]:-ms-input-placeholder,input[type="text"]:not(.input-addon-field):-ms-input-placeholder{color:#dedede}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:#000;border-color:rgba(82,168,236,0.8);outline:0;box-shadow:0 0 8px rgba(82,168,236,0.6)}textarea{padding:3px;border:1px solid #ccc;width:400px;max-width:99%;height:200px;font-family:sans-serif;font-size:1em}textarea::-webkit-input-placeholder{color:#dedede}textarea::-moz-placeholder{color:#dedede}textarea:-ms-input-placeholder{color:#dedede}select{font-size:1.0em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:bold}@media (max-width: 480px){.form-required{display:none}}input.form-error,textarea.form-error{border:2px solid #b94a48}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid #b94a48}.form-errors{color:#b94a48;list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:0.8em;color:brown;margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-login{max-width:350px;margin:8% auto 0}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:bold}.reset-password{margin-top:20px}.reset-password a{color:#999}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:#999;margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:rgba(147,128,108,0.1);color:#666;font:inherit;font-weight:normal}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width: 480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,0.25);padding:4px 0.75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-normal{color:#333;background-color:#f0f0f0;border-color:#ddd}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:normal;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid #ddd;background:#f5f5f5;color:#333}.btn:hover,.btn:focus{border-color:#bbb;background:#fafafa;color:#000}.btn-red{border-color:#b0281a;background:#d14836;color:#fff}.btn-red:hover,.btn-red:focus{border-color:#b0281a;background:#c53727;color:#fff}.btn-blue{border-color:#3079ed;background:#4d90fe;color:#fff}.btn-blue:hover,.btn-blue:focus{border-color:#3079ed;background:#357ae8;color:#fff}.btn:disabled{color:#ccc;border-color:#ccc;background:#f7f7f7}.buttons-header{font-size:0.8em;margin-top:5px;margin-bottom:15px}.tooltip-arrow:after{background:#fff;border:1px solid #aaaaaa;box-shadow:0 0 5px #aaa}div.ui-tooltip{min-width:200px;max-width:600px}.tooltip-arrow{width:20px;height:10px;overflow:hidden;position:absolute}.tooltip-arrow.top{top:-10px}.tooltip-arrow.bottom{bottom:-10px}.tooltip-arrow.align-left{left:10px}.tooltip-arrow.align-right{right:10px}.tooltip-arrow:after{content:"";position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.tooltip-arrow.bottom:after{top:-10px}.tooltip-arrow.top:after{bottom:-10px}.tooltip-arrow.align-left:after{left:0}.tooltip-arrow.align-right:after{right:0}.tooltip-large{width:600px}.ui-tooltip-content .markdown p{margin-bottom:0}.ui-tooltip li{list-style-type:none}.tooltip .fa-info-circle{color:#999}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:#fff;border:1px solid #b2b2b2;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,0.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:0.9em;border-bottom:1px solid #f8f8f8;cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:#333}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:#333;text-decoration:none}.dropdown-menu-link-text:hover{text-decoration:underline}.accordion-title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEQ5RDgxQzc2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEQ5RDgxQzg2RjQ5MTFFMjhEMUNENzFGRUMwRjhBRTciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0RDlEODFDNTZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0RDlEODFDNjZGNDkxMUUyOEQxQ0Q3MUZFQzBGOEFFNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvXFWFAAAAAYSURBVHjaYvj//z8D0/Pnz/8zgFgAAQYAS5UJscReGMIAAAAASUVORK5CYII=) repeat-x scroll 0 10px}.accordion-title h3{display:inline;padding-right:5px;background:#fff}.accordion-content{margin-top:15px;margin-bottom:25px}.accordion-toggle{color:#333;text-decoration:none}.accordion-toggle:focus{color:#333}.accordion-toggle:hover{color:#999}.accordion-toggle:before{content:"\f0d7"}.accordion-collapsed{margin-bottom:25px}.accordion-collapsed .accordion-toggle:before{content:"\f0da"}.accordion-collapsed .accordion-content{display:none}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:#555;cursor:pointer;border-bottom:1px solid #f8f8f8;line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid #ccc;border-radius:5px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:#555;position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:#555;position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:#333;font-weight:bold;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:#999;font-weight:normal}#main .confirm{max-width:700px}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 50px);top:2%;left:50%;transform:translateX(-50%);background:#fff;overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:#333}#modal-close-button:hover{color:#b94a48}.pagination{text-align:center}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-top:5px;margin-bottom:5px;border-bottom:1px solid #dedede}header>*{box-sizing:border-box}header>*{width:1%}header .menus-container{width:10%}@media (min-width: 768px) and (max-width: 1150px){header .menus-container{width:15%}}@media (max-width: 768px){header .menus-container{width:30%;order:2}}header .board-selector-container{width:25%}@media (min-width: 768px) and (max-width: 1150px){header .board-selector-container{width:20%}}@media (max-width: 768px){header .board-selector-container{width:70%;order:1;margin-bottom:5px}}header .title-container{width:65%}@media (max-width: 768px){header .title-container{width:100%;order:3}}header h1{font-size:1.5em}header h1 .tooltip{opacity:0.3;font-size:0.7em}.web-notification-icon{color:#36c}.web-notification-icon:focus,.web-notification-icon:hover{color:#000}.logo a{opacity:0.5;color:#d40000;text-decoration:none}.logo span{color:#333}.logo a:hover{opacity:0.8;color:#333}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:bold;border-bottom:1px dotted #ccc}.page-header h2 a{color:#333;text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:#999}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width: 480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:#333;text-decoration:none;font-weight:bold}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:bold;color:#000;text-decoration:none}.sidebar-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.sidebar-container>*{box-sizing:border-box}.sidebar-container>*{width:1%}.sidebar-content{padding-left:10px;width:82%}@media (max-width: 480px){.sidebar-content{width:100%}}.sidebar{max-width:240px;min-width:190px;width:18%}@media (max-width: 480px){.sidebar{width:100%;max-width:99%;min-width:0}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:#999;font-weight:300}.sidebar>ul a:hover{color:#333}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted #efefef;padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:#333;font-weight:bold}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:#555}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:#fff;text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:bold;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,0.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:0.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:0.9em;color:#555}.file-thumbnail-description{font-size:0.8em;color:#999;margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:180px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:800px}.action-menu{color:#333;text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width: 480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width: 480px){.project-overview-column{text-align:left}}.project-overview-column small{color:#999}.project-overview-column strong{color:#555;display:block}@media (max-width: 480px){.project-overview-column strong{display:inline}}.project-header{box-sizing:border-box;display:flex;flex-wrap:wrap;margin-bottom:8px}.project-header>*{box-sizing:border-box}.project-header>*{width:1%}.project-header .dropdown-component{margin-top:4px;width:5%}@media (min-width: 768px) and (max-width: 1150px){.project-header .dropdown-component{width:8%}}@media (max-width: 768px){.project-header .dropdown-component{width:100%}}.project-header .views-switcher-component{margin-top:4px;width:38%}@media (max-width: 1300px){.project-header .views-switcher-component{width:45%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .views-switcher-component{width:92%}}@media (max-width: 768px){.project-header .views-switcher-component{margin-top:0;width:100%}}.project-header .filter-box-component{margin:0;width:55%}@media (max-width: 1300px){.project-header .filter-box-component{width:50%}}@media (min-width: 768px) and (max-width: 1150px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}@media (max-width: 768px){.project-header .filter-box-component{width:100%;margin-top:10px}.project-header .filter-box-component .filter-box{max-width:100%}}.project-header .filter-box-component form{margin:0}.views{display:inline-block;margin-right:10px;font-size:0.9em}@media (max-width: 560px){.views{width:100%}}@media (max-width: 768px){.views{margin-top:10px;font-size:1em}}@media (max-width: 480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:#fafafa;border:1px solid #ddd;border-right:none;padding:4px 8px;display:inline}@media (max-width: 560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid #ddd}}.views li.active a{font-weight:bold;color:#000;text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid #ddd;border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:#555;text-decoration:none}.views a:hover{color:#333;text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:#999}.dashboard-table-link{font-weight:bold;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:#999}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board th.board-column-header{width:240px}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast: active), (-ms-high-contrast: none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}td.board-column-task-collapsed{font-weight:bold;background-color:#fbfbfb}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;-webkit-backface-visibility:hidden;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:0 100%;-moz-transform-origin:0 100%;-ms-transform-origin:0 100%;transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:#36c;font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:#999;font-weight:normal}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-limit{background-color:#DF5353}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed #000;background:#fafafa;height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:0.9em}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board .dropdown-menu{font-weight:bold}.task-board .task-score{font-weight:bold}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:0.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{font-weight:500;color:#000;border:1px solid #555;padding:1px 2px 1px 2px;border-radius:4px}.task-board-category:hover{opacity:0.6}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:0.6}.task-board-icons{font-size:0.8em;text-align:right;margin-top:4px;margin-bottom:2px}.task-board-icons a{opacity:0.5}.task-board-icons span{opacity:0.5;margin-left:2px}.task-board-icons a:hover,.task-board-icons span:hover{opacity:1.0}.flag-milestone{color:green}.task-board-age{display:inline-block}span.task-board-age-total{border:#666 1px solid;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-board-age-column{border:#666 1px solid;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board-date{font-weight:bold;color:#000}span.task-board-date-today{opacity:1.0;color:#36c}span.task-board-date-overdue{opacity:1.0;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:#333;border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}#task-summary{margin-bottom:15px}#task-summary h2{color:#555;font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:15px}.task-summary-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}@media (max-width: 480px){.task-summary-columns{display:block}}.task-summary-column{color:#333}.task-summary-column span{color:#555}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width: 1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width: 1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width: 1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.task-table .dropdown-menu{color:#000;text-decoration:none;font-weight:bold}.task-table .dropdown-menu:focus,.task-table .dropdown-menu:hover{text-decoration:underline}td.task-table a{color:#000;text-decoration:none}td.task-table a:hover{text-decoration:underline}.comment-sorting{text-align:right}.comment-sorting a{color:#555;font-weight:normal;text-decoration:none}.comment-sorting a:hover{color:#999}.comment{padding:5px;margin-bottom:15px}.comment:hover{background:#fafafa}.comment-title{border-bottom:1px dotted #eee;margin-left:55px;margin-bottom:10px}.comment-date{color:#999;font-weight:200}.comment-actions{font-size:0.8em;margin-left:55px;margin-top:8px}.comment-actions li{display:inline}.comment-actions a{color:#999;text-decoration:none}.comment-actions a:focus,.comment-actions a:hover{color:#333;text-decoration:underline}.comment-content{margin-left:55px}.subtasks-table td{vertical-align:middle}.task-links-table td{vertical-align:middle}.task-links-task-count{color:#999}.task-link-closed{text-decoration:line-through}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:#999;text-decoration:none;margin-right:10px}.text-editor a:hover{color:#36c}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:bold}.markdown h2{font-weight:bold}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;color:#555}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.documentation{margin:0 auto;padding:20px;max-width:850px;background:#fefefe;border:1px solid #ccc;border-radius:5px;color:#555}.documentation img{border:1px solid #333}.documentation h1{text-decoration:none;margin-bottom:30px}.documentation h2{text-decoration:none;border-bottom:1px solid #ccc;margin-bottom:25px}.documentation li{line-height:30px}.listing{border-radius:4px;padding:8px 35px 8px 14px;margin-bottom:20px;border:1px solid #ddd;color:#333;background-color:#fcfcfc;overflow:auto}.listing li{list-style-type:square;margin-left:20px;margin-bottom:3px}.listing ul{margin-top:15px;margin-bottom:15px}.activity-event{margin-bottom:15px;padding:10px}.activity-event:hover{background:#fafafa}.activity-date{margin-left:10px;font-weight:normal;color:#999}.activity-content{margin-left:55px}.activity-title{font-weight:bold;color:#000;border-bottom:1px dotted #efefef}.activity-description{color:#555;margin-top:10px}@media (max-width: 480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}div.ganttview-hzheader-month,div.ganttview-hzheader-day,div.ganttview-vtheader,div.ganttview-vtheader-item-name,div.ganttview-vtheader-series,div.ganttview-grid,div.ganttview-grid-row-cell{float:left}div.ganttview-hzheader-month,div.ganttview-hzheader-day{text-align:center}div.ganttview-grid-row-cell.last,div.ganttview-hzheader-day.last,div.ganttview-hzheader-month.last{border-right:none}div.ganttview{border:1px solid #999}div.ganttview-hzheader-month{width:60px;height:20px;border-right:1px solid #d0d0d0;line-height:20px;overflow:hidden}div.ganttview-hzheader-day{width:20px;height:20px;border-right:1px solid #f0f0f0;border-top:1px solid #d0d0d0;line-height:20px;color:#555}div.ganttview-vtheader{margin-top:41px;width:400px;overflow:hidden;background-color:#fff}div.ganttview-vtheader-item{color:#555}div.ganttview-vtheader-series-name{width:400px;height:31px;line-height:31px;padding-left:3px;border-top:1px solid #d0d0d0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}div.ganttview-vtheader-series-name a{color:#555;text-decoration:none}div.ganttview-vtheader-series-name a:hover{color:#333;text-decoration:underline}div.ganttview-vtheader-series-name a i{color:#000}div.ganttview-vtheader-series-name a:hover i{color:#555}div.ganttview-slide-container{overflow:auto;border-left:1px solid #999}div.ganttview-grid-row-cell{width:20px;height:31px;border-right:1px solid #f0f0f0;border-top:1px solid #f0f0f0}div.ganttview-grid-row-cell.ganttview-weekend{background-color:#fafafa}div.ganttview-blocks{margin-top:40px}div.ganttview-block-container{height:28px;padding-top:4px}div.ganttview-block{position:relative;height:25px;background-color:#E5ECF9;border:1px solid #c0c0c0;border-radius:3px}.ganttview-block-movable{cursor:move}div.ganttview-block-not-defined{border-color:#000;background-color:#000}div.ganttview-block-text{position:absolute;height:12px;font-size:0.7em;color:#999;padding:2px 3px}div.ganttview-block div.ui-resizable-handle.ui-resizable-s{bottom:-0}.user-mention-link{font-weight:bold;color:#000;text-decoration:none}.user-mention-link:hover{color:#555}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:0.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:0.6}.slideshow-icon:hover{opacity:0.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em} diff --git a/assets/js/app.min.js b/assets/js/app.min.js index 274b6ded..015f4a25 100644 --- a/assets/js/app.min.js +++ b/assets/js/app.min.js @@ -1,3 +1,3 @@ -!function(){function t(t,a,i){if(!o)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var r=i&&i.debug||!1;if(r){var s=document.querySelector("#input-textarea-caret-position-mirror-div");s&&s.parentNode.removeChild(s)}var d=document.createElement("div");d.id="input-textarea-caret-position-mirror-div",document.body.appendChild(d);var l=d.style,c=window.getComputedStyle?getComputedStyle(t):t.currentStyle;l.whiteSpace="pre-wrap","INPUT"!==t.nodeName&&(l.wordWrap="break-word"),l.position="absolute",r||(l.visibility="hidden"),e.forEach(function(t){l[t]=c[t]}),n?t.scrollHeight>parseInt(c.height)&&(l.overflowY="scroll"):l.overflow="hidden",d.textContent=t.value.substring(0,a),"INPUT"===t.nodeName&&(d.textContent=d.textContent.replace(/\s/g," "));var u=document.createElement("span");u.textContent=t.value.substring(a)||".",d.appendChild(u);var p={top:u.offsetTop+parseInt(c.borderTopWidth),left:u.offsetLeft+parseInt(c.borderLeftWidth)};return r?u.style.backgroundColor="#aaa":document.body.removeChild(d),p}var e=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],o="undefined"!=typeof window,n=o&&null!=window.mozInnerScreenX;"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=t:o&&(window.getCaretCoordinates=t)}(),function(){function t(){if(!("KeyboardEvent"in window)||"key"in KeyboardEvent.prototype)return!1;var t={get:function(t){var e=o.keys[this.which||this.keyCode];return Array.isArray(e)&&(e=e[+this.shiftKey]),e}};return Object.defineProperty(KeyboardEvent.prototype,"key",t),t}var e,o={polyfill:t,keys:{3:"Cancel",6:"Help",8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",28:"Convert",29:"NonConvert",30:"Accept",31:"ModeChange",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",41:"Select",42:"Print",43:"Execute",44:"PrintScreen",45:"Insert",46:"Delete",48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],91:"OS",93:"ContextMenu",144:"NumLock",145:"ScrollLock",181:"VolumeMute",182:"VolumeDown",183:"VolumeUp",186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"'],224:"Meta",225:"AltGraph",246:"Attn",247:"CrSel",248:"ExSel",249:"EraseEof",250:"Play",251:"ZoomOut"}};for(e=1;e<25;e++)o.keys[111+e]="F"+e;var n="";for(e=65;e<91;e++)n=String.fromCharCode(e),o.keys[e]=[n.toLowerCase(),n.toUpperCase()];o.polyfill()}(),Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(t){for(var e=(this.document||this.ownerDocument).querySelectorAll(t),o=e.length;--o>=0&&e.item(o)!==this;);return o>-1});var KB={components:{},utils:{},html:{},http:{},listeners:{clicks:{},changes:{},keys:{},internals:{}}};KB.on=function(t,e){this.listeners.internals.hasOwnProperty(t)||(this.listeners.internals[t]=[]),this.listeners.internals[t].push(e)},KB.trigger=function(t,e){if(this.listeners.internals.hasOwnProperty(t))for(var o=0;o<this.listeners.internals[t].length&&this.listeners.internals[t][o](e);o++);},KB.onClick=function(t,e){this.listeners.clicks[t]=e},KB.onChange=function(t,e){this.listeners.changes[t]=e},KB.onKey=function(t,e,o){this.listeners.keys[t]={callback:e,ignoreInputField:o||!1}},KB.listen=function(){function t(t){for(var e in n.listeners.clicks)n.listeners.clicks.hasOwnProperty(e)&&t.target.matches(e)&&(t.preventDefault(),n.listeners.clicks[e](t))}function e(t){for(var e in n.listeners.changes)n.listeners.changes.hasOwnProperty(e)&&t.target.matches(e)&&n.listeners.changes[e](t.target)}function o(t){var e=KB.utils.getKey(t),o=KB.utils.isInputField(t);if(o&&["Escape","Meta","Enter","Control"].indexOf(e)===-1||a.push(e),a.length>0){var i=!0;for(var r in n.listeners.keys)if(n.listeners.keys.hasOwnProperty(r)){var s=n.listeners.keys[r],d=r.split("+");if(KB.utils.arraysIdentical(a,d)){if(o&&!s.ignoreInputField)return void(a=[]);t.preventDefault(),t.stopPropagation(),a=[],s.callback(t);break}KB.utils.arraysStartsWith(a,d)&&(i=!1)}i&&(a=[])}}var n=this,a=[];document.addEventListener("click",t,!1),document.addEventListener("change",e,!1),window.addEventListener("keydown",o,!1)},KB.component=function(t,e){this.components[t]=e},KB.getComponent=function(t,e,o){var n=this.components[t];return new n(e,o)},KB.render=function(){for(var t in this.components)for(var e=document.querySelectorAll(".js-"+t),o=0;o<e.length;o++)if(this.components.hasOwnProperty(t)){var n;e[o].dataset.params&&(n=JSON.parse(e[o].dataset.params));var a=KB.getComponent(t,e[o],n);a.render(),e[o].className=e[o].className+"-rendered"}},KB.interval=function(t,e){setInterval(e,1e3*t)},KB.dom=function(t){function e(t){var e="string"==typeof t?document.createElement(t):t;this.attr=function(t,o){return null!==o&&"undefined"!=typeof o?(e.setAttribute(t,o),this):e.getAttribute(t)},this.data=function(t,o){return 1===arguments.length?e.dataset[t]:(e.dataset[t]=o,this)},this.hide=function(){return e.style.display="none",this},this.show=function(){return e.style.display="block",this},this.toggle=function(){return"none"===e.style.display?this.show():this.hide(),this},this.style=function(t,o){return e.style[t]=o,this},this.on=function(t,o,n){return e.addEventListener(t,function(t){n||t.preventDefault(),o(t.target)}),this},this.click=function(t){return this.on("click",t)},this.mouseover=function(t){return this.on("mouseover",t)},this.change=function(t){return this.on("change",t)},this.add=function(t){return e.appendChild(t),this},this.replace=function(t){return e.parentNode.replaceChild(t,e),this},this.html=function(t){return e.innerHTML=t,this},this.text=function(t){return e.appendChild(document.createTextNode(t)),this},this.addClass=function(t){return e.classList.add(t),this},this.removeClass=function(t){return e.classList.remove(t),this},this.toggleClass=function(t){return e.classList.toggle(t),this},this.hasClass=function(t){return e.classList.contains(t)},this.disable=function(){return e.disabled=!0,this},this.enable=function(){return e.disabled=!1,this},this.remove=function(){return e.parentNode.removeChild(e),this},this.parent=function(t){for(;e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null},this.find=function(t){return e.querySelector(t)},this["for"]=function(t,o){for(var n=0;n<o.length;n++){var a=o[n];if("object"!=typeof a)e.appendChild(KB.dom(t).text(a).build());else{var i=KB.dom(t);for(var r in a)a.hasOwnProperty(r)&&r in this&&"function"==typeof this[r]?i[r](a[r]):i.attr(r,a[r]);e.appendChild(i.build())}}return this},this.build=function(){return e}}return new e(t)},KB.find=function(t){var e=document.querySelector(t);return e?KB.dom(e):null},KB.exists=function(t){return!!document.querySelector(t)},KB.focus=function(t){var e=document.querySelector(t);if(e)return e.focus()},KB.html.label=function(t,e){return KB.dom("label").attr("for",e).text(t).build()},KB.html.radio=function(t,e,o){return KB.dom("label").add(KB.dom("input").attr("type","radio").attr("name",e).attr("value",o).build()).text(t).build()},KB.html.radios=function(t){var e=KB.dom("div");for(var o in t)t.hasOwnProperty(o)&&e.add(KB.html.radio(o.label,o.name,o.value))},KB.http.request=function(t,e,o,n){function a(t){try{return JSON.parse(t.responseText)}catch(e){return t.responseText}}var i=function(){},r=function(){};this.execute=function(){var s=new XMLHttpRequest;s.open(t,e,!0),s.setRequestHeader("X-Requested-With","XMLHttpRequest");for(var d in o)o.hasOwnProperty(d)&&s.setRequestHeader(d,o[d]);return s.onerror=function(){r()},s.onreadystatechange=function(){if(s.readyState===XMLHttpRequest.DONE){var t=a(s);200===s.status?i(t):r(t)}},s.send(n),this},this.success=function(t){return i=t,this},this.error=function(t){return r=t,this}},KB.http.get=function(t){return new KB.http.request("GET",t).execute()},KB.http.postJson=function(t,e){var o={"Content-Type":"application/json",Accept:"application/json"};return new KB.http.request("POST",t,o,JSON.stringify(e)).execute()},KB.utils.formatDuration=function(t){return t>=86400?Math.round(t/86400)+"d":t>=3600?Math.round(t/3600)+"h":t>=60?Math.round(t/60)+"m":t+"s"},KB.utils.getSelectionPosition=function(t){var e,o;return e=t.value.length<t.selectionStart?t.value.length:t.selectionStart,o=t.selectionStart===t.selectionEnd?e:t.selectionEnd,{selectionStart:e,selectionEnd:o}},KB.utils.arraysIdentical=function(t,e){var o=t.length;if(o!==e.length)return!1;for(;o--;)if(t[o]!==e[o])return!1;return!0},KB.utils.arraysStartsWith=function(t,e){for(var o=Math.min(t.length,e.length),n=0;n<o;n++)if(t[n]!==e[n])return!1;return!0},KB.utils.isInputField=function(t){var e=t.target;return!("INPUT"!==e.tagName&&"SELECT"!==e.tagName&&"TEXTAREA"!==e.tagName&&!e.isContentEditable)},KB.utils.getKey=function(t){var e={Esc:"Escape",Up:"ArrowUp",Down:"ArrowDown",Left:"ArrowLeft",Right:"ArrowRight"};for(var o in e)if(e.hasOwnProperty(o)&&o===t.key)return e[o];return t.key},KB.onClick(".accordion-toggle",function(t){var e=KB.dom(t.target).parent(".accordion-section");e&&(KB.dom(e).toggleClass("accordion-collapsed"),KB.dom(KB.dom(e).find(".accordion-content")).toggle())}),KB.component("calendar",function(t,e){this.render=function(){var o=$(t);o.fullCalendar({locale:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(t){$.ajax({cache:!1,url:e.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t.id,date_due:t.start.format()})})},viewRender:function(){var t=e.checkUrl,n={start:o.fullCalendar("getView").start.format(),end:o.fullCalendar("getView").end.format()};for(var a in n)t+="&"+a+"="+n[a];$.getJSON(t,function(t){o.fullCalendar("removeEvents"),o.fullCalendar("addEventSource",t),o.fullCalendar("rerenderEvents")})}})}}),KB.component("chart-project-avg-time-column",function(t,e){this.render=function(){var o=e.metrics,n=[e.label],a=[];for(var i in o)n.push(o[i].average),a.push(o[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[n],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("chart-project-burndown",function(t,e){this.render=function(){for(var o=e.metrics,n=[[e.labelTotal]],a=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(e.dateFormat),s=0;s<o.length;s++)for(var d=0;d<o[s].length;d++)0===s?n.push([o[s][d]]):(n[d+1].push(o[s][d]),d>0&&(void 0===n[0][s]&&n[0].push(0),n[0][s]+=o[s][d]),0===d&&a.push(r(i.parse(o[s][d]))));KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n},axis:{x:{type:"category",categories:a}}})}}),KB.component("chart-project-cumulative-flow",function(t,e){this.render=function(){for(var o=e.metrics,n=[],a=[],i=[],r=d3.time.format("%Y-%m-%d"),s=d3.time.format(e.dateFormat),d=0;d<o.length;d++)for(var l=0;l<o[d].length;l++)0===d?(n.push([o[d][l]]),l>0&&a.push(o[d][l])):(n[l].push(o[d][l]),0===l&&i.push(s(r.parse(o[d][l]))));KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n,type:"area-spline",groups:[a]},axis:{x:{type:"category",categories:i}}})}}),KB.component("chart-project-lead-cycle-time",function(t,e){this.render=function(){var o=e.metrics,n=[e.labelCycle],a=[e.labelLead],i=[],r={};r[e.labelCycle]="area",r[e.labelLead]="area-spline";var s={};s[e.labelLead]="#afb42b",s[e.labelCycle]="#4e342e";for(var d=0;d<o.length;d++)n.push(parseInt(o[d].avg_cycle_time)),a.push(parseInt(o[d].avg_lead_time)),i.push(o[d].day);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[a,n],types:r,colors:s},axis:{x:{type:"category",categories:i},y:{tick:{format:KB.utils.formatDuration}}}})}}),KB.component("chart-project-task-distribution",function(t,e){this.render=function(){for(var o=[],n=0;n<e.metrics.length;n++)o.push([e.metrics[n].column_title,e.metrics[n].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o,type:"donut"}})}}),KB.component("chart-project-time-comparison",function(t,e){this.render=function(){var o=[e.labelSpent],n=[e.labelEstimated],a=[];for(var i in e.metrics)o.push(e.metrics[i].time_spent),n.push(e.metrics[i].time_estimated),a.push("open"===i?e.labelOpen:e.labelClosed);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[o,n],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:a}},legend:{show:!0}})}}),KB.component("chart-project-user-distribution",function(t,e){this.render=function(){for(var o=[],n=0;n<e.metrics.length;n++)o.push([e.metrics[n].user,e.metrics[n].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o,type:"donut"}})}}),KB.component("chart-task-time-column",function(t,e){this.render=function(){for(var o=e.metrics,n=[e.label],a=[],i=0;i<o.length;i++)n.push(o[i].time_spent),a.push(o[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[n],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("external-task-view",function(t,e){this.render=function(){$.ajax({cache:!1,url:e.url,success:function(e){KB.dom(t).html('<div id="external-task-view">'+e+"</div>")}})}}),KB.component("image-slideshow",function(t,e){function o(t){switch(KB.utils.getKey(t)){case"Escape":d();break;case"ArrowRight":i();break;case"ArrowLeft":r()}}function n(t){t.matches(".slideshow-next-icon")?i():t.matches(".slideshow-previous-icon")?r():t.matches(".slideshow-download-icon")?window.location.href=t.href:d()}function a(t){var e=KB.dom(t).data("imageId"),o=l(e);s(o)}function i(){d();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var o=t+1;o>=e.images.length&&(o=0),p=e.images[o];break}s()}function r(){d();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var o=t-1;o<0&&(o=e.images.length-1),p=e.images[o];break}s()}function s(){var t=KB.dom("div").attr("class","fa fa-window-close slideshow-icon slideshow-close-icon").build(),e=KB.dom("a").attr("class","fa fa-download slideshow-icon slideshow-download-icon").attr("href",c(p,"download")).build(),a=KB.dom("div").attr("class","fa fa-chevron-circle-left slideshow-icon slideshow-previous-icon").build(),i=KB.dom("div").attr("class","fa fa-chevron-circle-right slideshow-icon slideshow-next-icon").build(),r=KB.dom("img").attr("src",c(p,"image")).attr("alt",p.name).attr("title",p.name).style("maxHeight",window.innerHeight-50+"px").build(),s=KB.dom("figcaption").text(p.name).build(),d=KB.dom("figure").add(r).add(s).build(),l=KB.dom("div").addClass("image-slideshow-overlay").add(t).add(e).add(a).add(i).add(d).click(n).build();document.body.appendChild(l),document.addEventListener("keydown",o,!1)}function d(){var t=KB.find(".image-slideshow-overlay");null!==t&&(document.removeEventListener("keydown",o,!1),t.remove())}function l(t){for(var o=0;o<e.images.length;o++)if(e.images[o].id===t)return e.images[o];return null}function c(t,o){var n=new RegExp(e.regex,"g");return e.url[o].replace(n,t.id)}function u(t){return KB.dom("img").attr("src",c(t,"thumbnail")).attr("alt",t.name).attr("title",t.name).data("imageId",t.id).click(a).build()}var p;this.render=function(){p=e.image,t.appendChild(u(p))}}),KB.keyboardShortcuts=function(){function t(t){var e=KB.find(t);null!==e&&(window.location=e.attr("href"))}function e(){var t=$("form");1==t.length?t.submit():t.length>1&&("INPUT"===document.activeElement.tagName||"TEXTAREA"===document.activeElement.tagName?$(document.activeElement).parents("form").submit():_KB.get("Popover").isOpen()&&$("#popover-container form").submit())}KB.onKey("?",function(){_KB.get("Popover").open($("body").data("keyboard-shortcut-url"))}),KB.onKey("Escape",function(){KB.exists("#suggest-menu")||(_KB.get("Popover").close(),_KB.get("Dropdown").close())}),KB.onKey("Meta+Enter",e,!0),KB.onKey("Control+Enter",e,!0),KB.onKey("b",function(){KB.trigger("board.selector.open")}),KB.exists("#board")&&(KB.onKey("c",function(){_KB.get("BoardHorizontalScrolling").toggle()}),KB.onKey("s",function(){_KB.get("BoardCollapsedMode").toggle()}),KB.onKey("n",function(){_KB.get("Popover").open($("#board").data("task-creation-url"))})),KB.exists("#task-view")&&(KB.onKey("e",function(){_KB.get("Popover").open(KB.find("#task-view").data("editUrl"))}),KB.onKey("c",function(){_KB.get("Popover").open(KB.find("#task-view").data("commentUrl"))}),KB.onKey("s",function(){_KB.get("Popover").open(KB.find("#task-view").data("subtaskUrl"))}),KB.onKey("l",function(){_KB.get("Popover").open(KB.find("#task-view").data("internalLinkUrl"))})),KB.onKey("f",function(){KB.focus("#form-search")}),KB.onKey("r",function(){var t=$(".filter-reset").data("filter"),e=$("#form-search");e.val(t),$("form.search").submit()}),KB.onKey("v+o",function(){t("a.view-overview")}),KB.onKey("v+b",function(){t("a.view-board")}),KB.onKey("v+c",function(){t("a.view-calendar")}),KB.onKey("v+l",function(){t("a.view-listing")}),KB.onKey("v+g",function(){t("a.view-gantt")})},KB.onChange(".js-project-creation-select-options",function(t){var e=t.value;"0"===e?KB.find(".js-project-creation-options").hide():KB.find(".js-project-creation-options").show()}),KB.component("project-select-role",function(t,e){function o(t){s=!0,e.role=t.value,a(),n()}function n(){KB.http.postJson(e.url,{id:e.id,role:e.role}).success(function(){s=!1,d=!0,a()}).error(function(){s=!1,d=!1,l=!0,a()})}function a(){KB.dom(r).remove(),r=i(),t.appendChild(r)}function i(){var t=[],n=KB.dom("div");for(var a in e.roles)if(e.roles.hasOwnProperty(a)){var i={value:a,text:e.roles[a]};e.role===a&&(i.selected="selected"),t.push(i)}return n.add(KB.dom("select").change(o)["for"]("option",t).build()),s?(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-spinner fa-pulse fa-fw").build())):d?(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-success").build())):l&&(n.text(" "),n.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-error").build())),n.build()}var r,s=!1,d=!1,l=!1;this.render=function(){r=i(),t.appendChild(r)}}),KB.component("select-dropdown-autocomplete",function(t,e){function o(t){switch(KB.utils.getKey(t)){case"Escape":y.value="",m();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),c();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),u();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(),d()}}function n(){m(),v()}function a(t){KB.dom(t).hasClass("select-dropdown-menu-item")&&(KB.find(".select-dropdown-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function i(){d()}function r(e){t.contains(e.target)||(y.value="",m())}function s(){var t=KB.find("#select-dropdown-menu");null===t?v():m()}function d(){var t=KB.find(".select-dropdown-menu-item.active"),o=t.data("value");if(K.value=o,y.value=e.items[o],m(),e.redirect){var n=new RegExp(e.redirect.regex,"g");window.location=e.redirect.url.replace(n,o)}}function l(){for(var t=document.querySelectorAll(".select-dropdown-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function c(){var t=l();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function u(){var t=l();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function p(t){var o=[],n=Object.keys(t);e.sortByKeys&&n.sort();for(var a=0;a<n.length;a++)o.push({"class":"select-dropdown-menu-item",text:t[n[a]],"data-label":t[n[a]],"data-value":n[a]});return o}function h(t,o){for(var n=[],a=!1,i=0;i<o.length;i++)if(0===t.length||0===o[i]["data-label"].toLowerCase().indexOf(t.toLowerCase())){var r=o[i];"undefined"!=typeof e.defaultValue&&String(e.defaultValue)===r["data-value"]&&(r["class"]+=" active",a=!0),n.push(r)}return!a&&n.length>0&&(n[0]["class"]+=" active"),n}function f(){var t=h(y.value,p(e.items)),o=b.getBoundingClientRect();return 0===t.length?null:KB.dom("ul").attr("id","select-dropdown-menu").style("top",o.bottom+"px").style("left",o.left+"px").style("width",o.width+"px").style("maxHeight",window.innerHeight-o.bottom-20+"px").mouseover(a).click(i)["for"]("li",t).build()}function m(){var t=KB.find("#select-dropdown-menu");null!==t&&t.remove(),document.removeEventListener("keydown",o,!1),document.removeEventListener("click",r,!1)}function v(){var t=f();null!==t&&document.body.appendChild(t),document.addEventListener("keydown",o,!1),document.addEventListener("click",r,!1)}function g(){return e.defaultValue&&e.defaultValue in e.items?e.items[e.defaultValue]:e.placeholder?e.placeholder:""}var b,y,K;this.render=function(){var o=KB.dom("i").attr("class","fa fa-chevron-down select-dropdown-chevron").click(s).build();K=KB.dom("input").attr("type","hidden").attr("name",e.name).attr("value",e.defaultValue||"").build(),y=KB.dom("input").attr("type","text").attr("placeholder",g()).addClass("select-dropdown-input").style("width",t.offsetWidth-30+"px").on("focus",s).on("input",n,!0).build(),b=KB.dom("div").addClass("select-dropdown-input-container").add(K).add(y).add(o).build(),t.appendChild(b),e.onFocus&&e.onFocus.forEach(function(t){KB.on(t,function(){y.focus()})})}}),KB.interval(60,function(){var t=KB.find("body").data("statusUrl"),e=KB.find("body").data("loginUrl");null===KB.find(".form-login")&&KB.http.get(t).error(function(){window.location=e})}),KB.component("submit-cancel",function(t,e){function o(){r=!0,KB.find("#modal-submit-button").replace(i()),KB.trigger("modal.submit")}function n(){KB.trigger("modal.cancel"),_KB.get("Popover").close()}function a(){r=!1,KB.find("#modal-submit-button").replace(i())}function i(){var t=KB.dom("button").click(o).attr("id","modal-submit-button").attr("type","submit").attr("class","btn btn-blue");return r&&t.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),t.text(e.submitLabel).build()}var r=!1;this.render=function(){KB.on("modal.stop",a);var o=KB.dom("div").attr("class","form-actions").add(i()).text(" "+e.orLabel+" ").add(KB.dom("a").attr("href","#").click(n).text(e.cancelLabel).build()).build();t.appendChild(o)}}),KB.component("suggest-menu",function(t,e){function o(t){switch(KB.utils.getKey(t)){case"Escape":u();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),l();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),c();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(),i()}}function n(){i()}function a(t){KB.dom(t).hasClass("suggest-menu-item")&&(KB.find(".suggest-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function i(){t.focus();var e=KB.find(".suggest-menu-item.active"),o=e.data("value"),n=e.data("trigger"),a=t.value,i=r(t),s=n+o+" ",d=KB.utils.getSelectionPosition(t),l=a.substring(0,d.selectionStart-i.length),c=a.substring(d.selectionEnd),p=l.length+s.length;t.value=l+s+c,t.setSelectionRange(p,p),u()}function r(t){var e=t.value.substring(0,t.selectionEnd).split("\n"),o=e[e.length-1],n=o.split(" ");return n[n.length-1]}function s(){for(var t=[".popover-form","#popover-content","body"],e=0;e<t.length;e++){var o=document.querySelector(t[e]);if(null!==o)return o}return null}function d(){for(var t=document.querySelectorAll(".suggest-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function l(){var t=d();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function c(){var t=d();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function u(){var t=KB.find("#suggest-menu");null!==t&&t.remove(),document.removeEventListener("keydown",o,!1)}function p(t){var o=r(t),n=h(o,e.triggers);u(),null!==n&&f(n,o.substring(n.length),e.triggers[n])}function h(t,e){for(var o in e)if(e.hasOwnProperty(o)&&0===t.indexOf(o))return o;return null}function f(t,e,o){if("string"==typeof o){var n=new RegExp("SEARCH_TERM","g"),a=o.replace(n,e);KB.http.get(a).success(function(o){m(t,e,o)})}else m(t,e,o)}function m(t,e,o){o=v(e,o),o.length>0&&b(g(t,o))}function v(t,e){var o=[];if(0===t.length)return e;for(var n=0;n<e.length;n++)0===e[n].value.toLowerCase().indexOf(t.toLowerCase())&&o.push(e[n]);return o}function g(t,e){for(var o=[],n=0;n<e.length;n++){var a="suggest-menu-item";0===n&&(a+=" active"),o.push({"class":a,html:e[n].html,"data-value":e[n].value,"data-trigger":t})}return o}function b(e){var i=s(),r=getCaretCoordinates(t,t.selectionEnd),d=r.left+t.offsetLeft-t.scrollLeft,l=r.top+t.offsetTop-t.scrollTop+16;document.addEventListener("keydown",o,!1);var c=KB.dom("ul").attr("id","suggest-menu").click(n).mouseover(a).style("left",d+"px").style("top",l+"px")["for"]("li",e).build();i.appendChild(c)}this.render=function(){t.addEventListener("input",function(){p(this)})}}),KB.component("task-move-position",function(t,e){function o(t){var e=KB.dom(document).find("#"+t);return e?parseInt(e.options[e.selectedIndex].value):null}function n(){var t=o("form-swimlanes");return null===t?e.board[0].id:t}function a(){var t=o("form-columns");return null===t?e.board[0].columns[0].id:t}function i(){var t=o("form-position");return null===t?1:t}function r(){var t=KB.find("input[name=positionChoice]:checked");return t?t.value:"before"}function s(){var t=KB.dom(document).find("#form-columns");KB.dom(t).replace(p());var e=KB.dom(document).find("#form-tasks");KB.dom(e).replace(h())}function d(){var t=KB.dom(document).find("#form-tasks");KB.dom(t).replace(h())}function l(t){KB.trigger("modal.stop"),KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").attr("class","alert alert-error").text(t).build())}function c(){var t=i(),o=r();"after"===o&&t++,KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").build()),KB.http.postJson(e.saveUrl,{column_id:a(),swimlane_id:n(),position:t}).success(function(){window.location.reload(!0)}).error(function(t){t&&l(t.message)})}function u(){var t=[];return e.board.forEach(function(e){t.push({value:e.id,text:e.name})}),KB.dom("select").attr("id","form-swimlanes").change(s)["for"]("option",t).build()}function p(){var t=[],o=n();return e.board.forEach(function(e){o===e.id&&e.columns.forEach(function(e){t.push({value:e.id,text:e.title})})}),KB.dom("select").attr("id","form-columns").change(d)["for"]("option",t).build()}function h(){var t=[],o=n(),i=a(),r=KB.dom("div").attr("id","form-tasks");return e.board.forEach(function(e){o===e.id&&e.columns.forEach(function(e){i===e.id&&e.tasks.forEach(function(e){t.push({value:e.position,text:"#"+e.id+" - "+e.title})})})}),t.length>0&&r.add(KB.html.label(e.positionLabel,"form-position")).add(KB.dom("select").attr("id","form-position")["for"]("option",t).build()).add(KB.html.radio(e.beforeLabel,"positionChoice","before")).add(KB.html.radio(e.afterLabel,"positionChoice","after")),r.build()}this.render=function(){KB.on("modal.submit",c);var o=KB.dom("div").on("submit",c).add(KB.dom("div").attr("id","message-container").build()).add(KB.html.label(e.swimlaneLabel,"form-swimlanes")).add(u()).add(KB.html.label(e.columnLabel,"form-columns")).add(p()).add(h()).build();t.appendChild(o)}}),KB.component("text-editor",function(t,e){function o(){var t=KB.dom("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:'<i class="fa fa-pencil-square-o fa-fw"></i> '+e.labelWrite,click:function(){a()}}]).build();return m=KB.dom("div").attr("class","text-editor-preview-area markdown").build(),KB.dom("div").attr("class","text-editor-view-mode").add(t).add(m).hide().build()}function n(){var t=KB.dom("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:'<i class="fa fa-eye fa-fw"></i> '+e.labelPreview,click:function(){a()}},{href:"#",html:'<i class="fa fa-bold fa-fw"></i>',click:function(){s("**")}},{href:"#",html:'<i class="fa fa-italic fa-fw"></i>',click:function(){s("_")}},{href:"#",html:'<i class="fa fa-strikethrough fa-fw"></i>',click:function(){s("~~")}},{href:"#",html:'<i class="fa fa-quote-right fa-fw"></i>',click:function(){l("> ")}},{href:"#",html:'<i class="fa fa-list-ul fa-fw"></i>',click:function(){l("* ")}},{href:"#",html:'<i class="fa fa-code fa-fw"></i>',click:function(){d("```")}}]).build(),o=KB.dom("textarea");return o.attr("name",e.name),e.tabindex&&o.attr("tabindex",e.tabindex),e.required&&o.attr("required","required"),o.text(e.text),e.placeholder&&o.attr("placeholder",e.placeholder),p=o.build(),e.suggestOptions&&KB.getComponent("suggest-menu",p,e.suggestOptions).render(),KB.dom("div").attr("class","text-editor-write-mode").add(t).add(p).build()}function a(){KB.dom(m).html(marked(p.value,{sanitize:!0})),KB.dom(h).toggle(),KB.dom(f).toggle()}function i(){return p.value.substring(p.selectionStart,p.selectionEnd)}function r(t,e,o,n){return t.substring(0,e)+n+t.substring(o)}function s(t){var e=i();c(t+e+t),u(t)}function d(t){var e=i();c("\n"+t+"\n"+e+"\n"+t),u(t,2)}function l(t){var e=i();if(e.indexOf("\n")===-1)c("\n"+t+e);else{for(var o=e.split("\n"),n=0;n<o.length;n++)o[n].indexOf(t)===-1&&(o[n]=t+o[n]);c(o.join("\n"))}u(t,1)}function c(t){p.focus();var e=!1,o=KB.utils.getSelectionPosition(p);if(v=o.selectionStart,g=o.selectionEnd,document.queryCommandSupported("insertText")&&(e=document.execCommand("insertText",!1,t)),!e){try{document.execCommand("ms-beginUndoUnit")}catch(n){}p.value=r(p.value,v,g,t);try{document.execCommand("ms-endUndoUnit")}catch(n){}}}function u(t,e){e=e||0;var o=g+t.length+e;p.setSelectionRange(o,o)}var p,h,f,m,v,g;this.render=function(){f=n(),h=o(),t.appendChild(KB.dom("div").attr("class","text-editor").add(h).add(f).build()),e.autofocus&&p.focus()}}),document.addEventListener("DOMContentLoaded",function(){KB.render(),KB.listen(),KB.keyboardShortcuts()});var Kanboard={};Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(t){return this.controllers[t]},Kanboard.App.prototype.execute=function(){for(var t in Kanboard)if("App"!==t){var e=new Kanboard[t](this);this.controllers[t]=e,"function"==typeof e.execute&&e.execute(),"function"==typeof e.listen&&e.listen(),"function"==typeof e.focus&&e.focus()}this.focus(),this.datePicker(),this.autoComplete(),this.tagAutoComplete()},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(t){t.preventDefault()})},Kanboard.App.prototype.datePicker=function(){var t=$("body"),e=t.data("js-date-format"),o=t.data("js-time-format"),n=t.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[n]),$.timepicker.setDefaults($.timepicker.regional[n]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:e,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:e,timeFormat:o,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var t=$(this),e=t.data("dst-field"),o=t.data("dst-extra-field");""===$("#form-"+e).val()&&t.parent().find("button[type=submit]").attr("disabled","disabled"),t.autocomplete({source:t.data("search-url"),minLength:1, -select:function(n,a){$("input[name="+e+"]").val(a.item.id),o&&$("input[name="+o+"]").val(a.item[o]),t.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(t){return!!document.getElementById(t)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.App.prototype.isVisible=function(){var t="";return"undefined"!=typeof document.hidden?t="visibilityState":"undefined"!=typeof document.mozHidden?t="mozVisibilityState":"undefined"!=typeof document.msHidden?t="msVisibilityState":"undefined"!=typeof document.webkitHidden&&(t="webkitVisibilityState"),""===t||"visible"==document[t]},Kanboard.BoardCollapsedMode=function(t){this.app=t},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(e){$(".filter-display-mode").toggle(),t.app.get("BoardDragAndDrop").refresh(e)}})},Kanboard.BoardColumnView=function(t){this.app=t},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var t=this;$(document).on("click",".board-toggle-column-view",function(){t.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var t=this;$(".board-column-header").each(function(){var e=$(this).data("column-id");localStorage.getItem("hidden_column_"+e)&&t.hideColumn(e)})},Kanboard.BoardColumnView.prototype.toggle=function(t){localStorage.getItem("hidden_column_"+t)?this.showColumn(t):this.hideColumn(t)},Kanboard.BoardColumnView.prototype.hideColumn=function(t){$(".board-column-"+t+" .board-column-expanded").hide(),$(".board-column-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t+" .board-column-expanded").hide(),$(".board-column-header-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+t).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+t+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+t).height())}),localStorage.setItem("hidden_column_"+t,1)},Kanboard.BoardColumnView.prototype.showColumn=function(t){$(".board-column-"+t+" .board-column-expanded").show(),$(".board-column-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t+" .board-column-expanded").show(),$(".board-column-header-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t).removeClass("board-column-header-collapsed"),$(".board-column-"+t).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+t).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+t)},Kanboard.BoardHorizontalScrolling=function(t){this.app=t},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var t=this;$(document).on("click",".filter-toggle-scrolling",function(e){e.preventDefault(),t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var t=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==t?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(t){this.app=t},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var t=parseInt($("#board").attr("data-check-interval"));t>0&&window.setInterval(this.check.bind(this),1e3*t)}},Kanboard.BoardPolling.prototype.check=function(){if(this.app.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(e){t.app.get("BoardDragAndDrop").refresh(e)},304:function(){t.app.hideLoadingIcon()}}})}},Kanboard.BoardTask=function(t){this.app=t},Kanboard.BoardTask.prototype.listen=function(){var t=this;$(document).on("click",".task-board-change-assignee",function(e){e.preventDefault(),e.stopPropagation(),t.app.get("Popover").open($(this).data("url"))}),$(document).on("click",".task-board",function(t){"A"!=t.target.tagName&&"IMG"!=t.target.tagName&&(window.location=$(this).data("task-url"))})},Kanboard.Column=function(t){this.app=t},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,o){var n=o.item;n.removeClass("draggable-item-selected"),t.savePosition(n.data("column-id"),n.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(t,e){var o=$(".columns-table").data("save-position-url"),n=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:t,position:e}),complete:function(){n.app.hideLoadingIcon()}})},Kanboard.Dropdown=function(t){this.app=t},Kanboard.Dropdown.prototype.listen=function(){var t=this;$(document).on("click",function(){t.close()}),$(document).on("click","#popover-content",function(){t.close()}),$(document).on("click",".dropdown-menu",function(e){e.preventDefault(),e.stopImmediatePropagation(),t.close();var o=$(this).next("ul"),n=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"})),o.clone().appendTo("#dropdown");var a=$("#dropdown ul");a.addClass("dropdown-submenu-open");var i=a.outerHeight(),r=a.outerWidth();n.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+n.top<i?a.css("top",n.top+$(this).height()):a.css("top",n.top-i-5),n.left+r>$(window).width()?a.css("left",n.left-r+$(this).outerWidth()):a.css("left",n.left)}),$(document).on("click",".dropdown-submenu-open li",function(t){$(t.target).is("li")&&$(this).find("a:visible")[0].click()})},Kanboard.Dropdown.prototype.close=function(){$("#dropdown").remove()},Kanboard.Dropdown.prototype.onPopoverOpened=function(){this.close()},Kanboard.FileUpload=function(t){this.app=t,this.files=[],this.currentFile=0},Kanboard.FileUpload.prototype.onPopoverOpened=function(){var t=document.getElementById("file-dropzone"),e=this;t&&(t.ondragover=t.ondragenter=function(t){t.stopPropagation(),t.preventDefault()},t.ondrop=function(t){t.stopPropagation(),t.preventDefault(),e.files=t.dataTransfer.files,e.show(),$("#file-error-max-size").hide()},$(document).on("click","#file-browser",function(t){t.preventDefault(),$("#file-form-element").get(0).click()}),$(document).on("click","#file-upload-button",function(t){t.preventDefault(),e.currentFile=0,e.checkFiles()}),$("#file-form-element").change(function(){e.files=document.getElementById("file-form-element").files,e.show(),$("#file-error-max-size").hide()}))},Kanboard.FileUpload.prototype.show=function(){if($("#file-list").remove(),this.files.length>0){$("#file-upload-button").prop("disabled",!1),$("#file-dropzone-inner").hide();for(var t=jQuery("<ul>",{id:"file-list"}),e=0;e<this.files.length;e++){var o=jQuery("<span>",{id:"file-percentage-"+e}).append("(0%)"),n=jQuery("<progress>",{id:"file-progress-"+e,value:0}),a=jQuery("<li>",{id:"file-label-"+e}).append(n).append(" ").append(this.files[e].name).append(" ").append(o);t.append(a)}$("#file-dropzone").append(t)}else $("#file-dropzone-inner").show()},Kanboard.FileUpload.prototype.checkFiles=function(){for(var t=parseInt($("#file-dropzone").data("max-size")),e=0;e<this.files.length;e++)if(this.files[e].size>t)return $("#file-error-max-size").show(),$("#file-label-"+e).addClass("file-error"),void $("#file-upload-button").prop("disabled",!0);this.uploadFiles()},Kanboard.FileUpload.prototype.uploadFiles=function(){this.files.length>0&&this.uploadFile(this.files[this.currentFile])},Kanboard.FileUpload.prototype.uploadFile=function(t){var e=document.getElementById("file-dropzone"),o=e.dataset.url,n=new XMLHttpRequest,a=new FormData;n.upload.addEventListener("progress",this.updateProgress.bind(this)),n.upload.addEventListener("load",this.transferComplete.bind(this)),n.open("POST",o,!0),a.append("files[]",t),n.send(a)},Kanboard.FileUpload.prototype.updateProgress=function(t){t.lengthComputable&&($("#file-progress-"+this.currentFile).val(t.loaded/t.total),$("#file-percentage-"+this.currentFile).text("("+Math.floor(t.loaded/t.total*100)+"%)"))},Kanboard.FileUpload.prototype.transferComplete=function(){if(this.currentFile++,this.currentFile<this.files.length)this.uploadFile(this.files[this.currentFile]);else{var t=$("#file-upload-button");t.prop("disabled",!0),t.parent().hide(),$("#file-done").show()}},Kanboard.Gantt=function(t){this.app=t,this.data=[],this.options={container:"#gantt-chart",showWeekends:!0,allowMoves:!0,allowResizes:!0,cellWidth:21,cellHeight:31,slideWidth:1e3,vHeaderWidth:200}},Kanboard.Gantt.prototype.execute=function(){this.app.hasId("gantt-chart")&&this.show()},Kanboard.Gantt.prototype.saveRecord=function(t){this.app.showLoadingIcon(),$.ajax({cache:!1,url:$(this.options.container).data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify(t),complete:this.app.hideLoadingIcon.bind(this)})},Kanboard.Gantt.prototype.show=function(){this.data=this.prepareData($(this.options.container).data("records"));var t=Math.floor(this.options.slideWidth/this.options.cellWidth+5),e=this.getDateRange(t),o=e[0],n=e[1],a=$(this.options.container),i=jQuery("<div>",{"class":"ganttview"});i.append(this.renderVerticalHeader()),i.append(this.renderSlider(o,n)),a.append(i),jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child",a).addClass("last"),$(this.options.container).data("readonly")?(this.options.allowResizes=!1,this.options.allowMoves=!1):(this.listenForBlockResize(o),this.listenForBlockMove(o))},Kanboard.Gantt.prototype.renderVerticalHeader=function(){for(var t=jQuery("<div>",{"class":"ganttview-vtheader"}),e=jQuery("<div>",{"class":"ganttview-vtheader-item"}),o=jQuery("<div>",{"class":"ganttview-vtheader-series"}),n=0;n<this.data.length;n++){var a=jQuery("<span>").append(jQuery("<i>",{"class":"fa fa-info-circle tooltip",title:this.getVerticalHeaderTooltip(this.data[n])})).append(" ");"task"==this.data[n].type?a.append(jQuery("<a>",{href:this.data[n].link,title:this.data[n].title}).append(this.data[n].title)):a.append(jQuery("<a>",{href:this.data[n].board_link,title:$(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')).append(" ").append(jQuery("<a>",{href:this.data[n].gantt_link,title:$(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')).append(" ").append(jQuery("<a>",{href:this.data[n].link}).append(this.data[n].title)),o.append(jQuery("<div>",{"class":"ganttview-vtheader-series-name"}).append(a))}return e.append(o),t.append(e),t},Kanboard.Gantt.prototype.renderSlider=function(t,e){var o=jQuery("<div>",{"class":"ganttview-slide-container"}),n=this.getDates(t,e);return o.append(this.renderHorizontalHeader(n)),o.append(this.renderGrid(n)),o.append(this.addBlockContainers()),this.addBlocks(o,t),o},Kanboard.Gantt.prototype.renderHorizontalHeader=function(t){var e=jQuery("<div>",{"class":"ganttview-hzheader"}),o=jQuery("<div>",{"class":"ganttview-hzheader-months"}),n=jQuery("<div>",{"class":"ganttview-hzheader-days"}),a=0;for(var i in t)for(var r in t[i]){var s=t[i][r].length*this.options.cellWidth;a+=s,o.append(jQuery("<div>",{"class":"ganttview-hzheader-month",css:{width:s-1+"px"}}).append($.datepicker.regional[$("body").data("js-lang")].monthNames[r]+" "+i));for(var d in t[i][r])n.append(jQuery("<div>",{"class":"ganttview-hzheader-day"}).append(t[i][r][d].getDate()))}return o.css("width",a+"px"),n.css("width",a+"px"),e.append(o).append(n),e},Kanboard.Gantt.prototype.renderGrid=function(t){var e=jQuery("<div>",{"class":"ganttview-grid"}),o=jQuery("<div>",{"class":"ganttview-grid-row"});for(var n in t)for(var a in t[n])for(var i in t[n][a]){var r=jQuery("<div>",{"class":"ganttview-grid-row-cell"});this.options.showWeekends&&this.isWeekend(t[n][a][i])&&r.addClass("ganttview-weekend"),o.append(r)}var s=jQuery("div.ganttview-grid-row-cell",o).length*this.options.cellWidth;o.css("width",s+"px"),e.css("width",s+"px");for(var d=0;d<this.data.length;d++)e.append(o.clone());return e},Kanboard.Gantt.prototype.addBlockContainers=function(){for(var t=jQuery("<div>",{"class":"ganttview-blocks"}),e=0;e<this.data.length;e++)t.append(jQuery("<div>",{"class":"ganttview-block-container"}));return t},Kanboard.Gantt.prototype.addBlocks=function(t,e){for(var o=jQuery("div.ganttview-blocks div.ganttview-block-container",t),n=0,a=0;a<this.data.length;a++){var i=this.data[a],r=this.daysBetween(i.start,i.end)+1,s=this.daysBetween(e,i.start),d=jQuery("<div>",{"class":"ganttview-block-text"}),l=jQuery("<div>",{"class":"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(i),css:{width:r*this.options.cellWidth-9+"px","margin-left":s*this.options.cellWidth+"px"}}).append(d);r>=2&&d.append(i.progress),l.data("record",i),this.setBarColor(l,i),jQuery(o[n]).append(l),n+=1}},Kanboard.Gantt.prototype.getVerticalHeaderTooltip=function(t){var e="";if("task"==t.type)e="<strong>"+t.column_title+"</strong> ("+t.progress+")<br/>"+t.title;else{var o=["project-manager","project-member"];for(var n in o){var a=o[n];if(!jQuery.isEmptyObject(t.users[a])){var i=jQuery("<ul>");for(var r in t.users[a])r&&i.append(jQuery("<li>").append(t.users[a][r]));e+="<p><strong>"+$(this.options.container).data("label-"+a)+"</strong></p>"+i[0].outerHTML}}}return e},Kanboard.Gantt.prototype.getBarTooltip=function(t){var e="";return t.not_defined?e=$(this.options.container).data("label-not-defined"):("task"==t.type&&(e="<strong>"+t.progress+"</strong><br/>"+$(this.options.container).data("label-assignee")+" "+(t.assignee?t.assignee:"")+"<br/>"),e+=$(this.options.container).data("label-start-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.start)+"<br/>",e+=$(this.options.container).data("label-end-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.end)),e},Kanboard.Gantt.prototype.setBarColor=function(t,e){e.not_defined?t.addClass("ganttview-block-not-defined"):(t.css("background-color",e.color.background),t.css("border-color",e.color.border),"0%"!=e.progress&&t.append(jQuery("<div>",{css:{"z-index":0,position:"absolute",top:0,bottom:0,"background-color":e.color.border,width:e.progress,opacity:.4}})))},Kanboard.Gantt.prototype.listenForBlockResize=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).resizable({grid:this.options.cellWidth,handles:"e,w",delay:300,stop:function(){var o=jQuery(this);e.updateDataAndPosition(o,t),e.saveRecord(o.data("record"))}})},Kanboard.Gantt.prototype.listenForBlockMove=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).draggable({axis:"x",delay:300,grid:[this.options.cellWidth,this.options.cellWidth],stop:function(){var o=jQuery(this);e.updateDataAndPosition(o,t),e.saveRecord(o.data("record"))}})},Kanboard.Gantt.prototype.updateDataAndPosition=function(t,e){var o=jQuery("div.ganttview-slide-container",this.options.container),n=o.scrollLeft(),a=t.offset().left-o.offset().left-1+n,i=t.data("record");i.not_defined=!1,this.setBarColor(t,i);var r=Math.round(a/this.options.cellWidth),s=this.addDays(this.cloneDate(e),r);i.start=s;var d=t.outerWidth(),l=Math.round(d/this.options.cellWidth)-1;i.end=this.addDays(this.cloneDate(s),l),"task"===i.type&&l>0&&jQuery("div.ganttview-block-text",t).text(i.progress),t.attr("title",this.getBarTooltip(i)),t.data("record",i),t.css("top","").css("left","").css("position","relative").css("margin-left",a+"px")},Kanboard.Gantt.prototype.getDates=function(t,e){var o=[];o[t.getFullYear()]=[],o[t.getFullYear()][t.getMonth()]=[t];for(var n=t;this.compareDate(n,e)==-1;){var a=this.addDays(this.cloneDate(n),1);o[a.getFullYear()]||(o[a.getFullYear()]=[]),o[a.getFullYear()][a.getMonth()]||(o[a.getFullYear()][a.getMonth()]=[]),o[a.getFullYear()][a.getMonth()].push(a),n=a}return o},Kanboard.Gantt.prototype.prepareData=function(t){for(var e=0;e<t.length;e++){var o=new Date(t[e].start[0],t[e].start[1]-1,t[e].start[2],0,0,0,0);t[e].start=o;var n=new Date(t[e].end[0],t[e].end[1]-1,t[e].end[2],0,0,0,0);t[e].end=n}return t},Kanboard.Gantt.prototype.getDateRange=function(t){for(var e=new Date,o=new Date,n=0;n<this.data.length;n++){var a=new Date;a.setTime(Date.parse(this.data[n].start));var i=new Date;i.setTime(Date.parse(this.data[n].end)),0==n&&(e=a,o=i),1==this.compareDate(e,a)&&(e=a),this.compareDate(o,i)==-1&&(o=i)}return this.daysBetween(e,o)<t&&(o=this.addDays(this.cloneDate(e),t)),e.setDate(e.getDate()-1),[e,o]},Kanboard.Gantt.prototype.daysBetween=function(t,e){if(!t||!e)return 0;for(var o=0,n=this.cloneDate(t);this.compareDate(n,e)==-1;)o+=1,this.addDays(n,1);return o},Kanboard.Gantt.prototype.isWeekend=function(t){return t.getDay()%6==0},Kanboard.Gantt.prototype.cloneDate=function(t){return new Date(t.getTime())},Kanboard.Gantt.prototype.addDays=function(t,e){return t.setDate(t.getDate()+1*e),t},Kanboard.Gantt.prototype.compareDate=function(t,e){if(isNaN(t)||isNaN(e))throw new Error(t+" - "+e);if(t instanceof Date&&e instanceof Date)return t<e?-1:t>e?1:0;throw new TypeError(t+" - "+e)},Kanboard.Popover=function(t){this.app=t},Kanboard.Popover.prototype.listen=function(){var t=this;$(document).on("click",".popover",function(e){t.onClick(e)}),$(document).on("click",".close-popover",function(e){t.close(e)}),$(document).on("click","#popover-close-button",function(e){t.close(e)}),$(document).on("click","#popover-content",function(t){t.stopPropagation()})},Kanboard.Popover.prototype.onClick=function(t){t.preventDefault(),t.stopPropagation();var e=t.currentTarget||t.target,o=e.getAttribute("href");o||(o=e.getAttribute("data-href")),o&&this.open(o)},Kanboard.Popover.prototype.isOpen=function(){return $("#popover-container").size()>0},Kanboard.Popover.prototype.open=function(t){var e=this;e.isOpen()||$.get(t,function(t){$("body").prepend('<div id="popover-container"><div id="popover-content"><div id="popover-content-header"><a href="#" id="popover-close-button"><i class="fa fa-times"></i></a></div>'+t+"</div></div>"),e.executeOnOpenedListeners()})},Kanboard.Popover.prototype.close=function(t){this.isOpen()&&(t&&t.preventDefault(),$("#popover-container").remove(),this.executeOnClosedListeners())},Kanboard.Popover.prototype.ajaxReload=function(t,e,o){var n=e.getResponseHeader("X-Ajax-Redirect");"self"===n?window.location.reload():n&&n.indexOf("#")>-1?window.location=n.split("#")[0]:n?window.location=n:($("#popover-content").html(t),$("#popover-content input[autofocus]").focus(),o.executeOnOpenedListeners())},Kanboard.Popover.prototype.executeOnOpenedListeners=function(){for(var t in this.app.controllers){var e=this.app.get(t);"function"==typeof e.onPopoverOpened&&e.onPopoverOpened()}this.afterOpen()},Kanboard.Popover.prototype.executeOnClosedListeners=function(){for(var t in this.app.controllers){var e=this.app.get(t);"function"==typeof e.onPopoverClosed&&e.onPopoverClosed()}},Kanboard.Popover.prototype.afterOpen=function(){var t=this,e=$("#popover-content .popover-form");e&&e.on("submit",function(o){o.preventDefault(),$.ajax({type:"POST",url:e.attr("action"),data:e.serialize(),success:function(e,o,n){t.ajaxReload(e,n,t)},beforeSend:function(){var t=$('.popover-form button[type="submit"]');t.html('<i class="fa fa-spinner fa-pulse"></i> '+t.html()),t.attr("disabled",!0)}})}),$(document).on("click",".popover-link",function(e){e.preventDefault(),$.ajax({type:"GET",url:$(this).attr("href"),success:function(e,o,n){t.ajaxReload(e,n,t)}})}),$("#popover-content input[autofocus]").each(function(){$(this).focus()}),this.app.datePicker(),this.app.autoComplete(),this.app.tagAutoComplete(),KB.render()},Kanboard.Screenshot=function(t){this.app=t,this.pasteCatcher=null},Kanboard.Screenshot.prototype.onPopoverOpened=function(){this.app.hasId("screenshot-zone")&&this.initialize()},Kanboard.Screenshot.prototype.initialize=function(){this.destroy(),window.Clipboard||(this.pasteCatcher=document.createElement("div"),this.pasteCatcher.id="screenshot-pastezone",this.pasteCatcher.contentEditable="true",this.pasteCatcher.style.opacity=0,this.pasteCatcher.style.position="fixed",this.pasteCatcher.style.top=0,this.pasteCatcher.style.right=0,this.pasteCatcher.style.width=0,document.body.insertBefore(this.pasteCatcher,document.body.firstChild),this.pasteCatcher.focus(),document.addEventListener("click",this.setFocus.bind(this)),document.getElementById("screenshot-zone").addEventListener("click",this.setFocus.bind(this))),window.addEventListener("paste",this.pasteHandler.bind(this))},Kanboard.Screenshot.prototype.destroy=function(){null!==this.pasteCatcher?document.body.removeChild(this.pasteCatcher):document.getElementById("screenshot-pastezone")&&document.body.removeChild(document.getElementById("screenshot-pastezone")),document.removeEventListener("click",this.setFocus.bind(this)),this.pasteCatcher=null},Kanboard.Screenshot.prototype.setFocus=function(){null!==this.pasteCatcher&&this.pasteCatcher.focus()},Kanboard.Screenshot.prototype.pasteHandler=function(t){if(t.clipboardData&&t.clipboardData.items){var e=t.clipboardData.items;if(e)for(var o=0;o<e.length;o++)if(e[o].type.indexOf("image")!==-1){var n=e[o].getAsFile(),a=new FileReader,i=this;a.onload=function(t){i.createImage(t.target.result)},a.readAsDataURL(n)}}else setTimeout(this.checkInput.bind(this),100)},Kanboard.Screenshot.prototype.checkInput=function(){var t=this.pasteCatcher.childNodes[0];t&&"IMG"===t.tagName&&this.createImage(t.src),this.pasteCatcher.innerHTML=""},Kanboard.Screenshot.prototype.createImage=function(t){var e=new Image;e.src=t,e.onload=function(){var e=t.split("base64,"),o=e[1];$("input[name=screenshot]").val(o)};var o=document.getElementById("screenshot-zone");o.innerHTML="",o.className="screenshot-pasted",o.appendChild(e),this.destroy(),this.initialize()},Kanboard.Search=function(t){this.app=t},Kanboard.Search.prototype.focus=function(){$(document).on("focus","#form-search",function(){var t=$("#form-search");if(t[0].setSelectionRange){var e=2*t.val().length;t[0].setSelectionRange(e,e)}})},Kanboard.Search.prototype.listen=function(){$(document).on("click",".filter-helper",function(t){t.preventDefault();var e=$(this).data("filter"),o=$(this).data("append-filter"),n=$(this).data("unique-filter"),a=$("#form-search");if(n){var i=n.substr(0,n.indexOf(":"));e=a.val().replace(new RegExp("("+i+":[#a-z0-9]+)","g"),""),e=e.replace(new RegExp("("+i+':"(.+)")',"g"),""),e=e.trim(),e+=" "+n}else o&&(e=a.val()+" "+o);a.val(e),$("form.search").submit()})},Kanboard.Subtask=function(t){this.app=t},Kanboard.Subtask.prototype.listen=function(){var t=this;this.dragAndDrop(),$(document).on("click",".subtask-toggle-status",function(e){var o=$(this);e.preventDefault(),$.ajax({cache:!1,url:o.attr("href"),success:function(e){o.hasClass("subtask-refresh-table")?$(".subtasks-table").replaceWith(e):o.replaceWith(e),t.dragAndDrop()}})}),$(document).on("click",".subtask-toggle-timer",function(e){var o=$(this);e.preventDefault(),$.ajax({cache:!1,url:o.attr("href"),success:function(e){$(".subtasks-table").replaceWith(e),t.dragAndDrop()}})})},Kanboard.Subtask.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".subtasks-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,o){var n=o.item;n.removeClass("draggable-item-selected"),t.savePosition(n.data("subtask-id"),n.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Subtask.prototype.savePosition=function(t,e){var o=$(".subtasks-table").data("save-position-url"),n=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({subtask_id:t,position:e}),complete:function(){n.app.hideLoadingIcon()}})},Kanboard.Swimlane=function(t){this.app=t},Kanboard.Swimlane.prototype.execute=function(){$(".swimlanes-table").length&&this.dragAndDrop()},Kanboard.Swimlane.prototype.listen=function(){var t=this;$(document).on("click",".board-swimlane-toggle",function(e){e.preventDefault();var o=$(this).data("swimlane-id");t.isCollapsed(o)?t.expand(o):t.collapse(o)})},Kanboard.Swimlane.prototype.onBoardRendered=function(){for(var t=this.getAllCollapsed(),e=0;e<t.length;e++)this.collapse(t[e])},Kanboard.Swimlane.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")},Kanboard.Swimlane.prototype.expand=function(t){var e=this.getAllCollapsed(),o=e.indexOf(t);o>-1&&e.splice(o,1),localStorage.setItem(this.getStorageKey(),JSON.stringify(e)),$(".board-swimlane-columns-"+t).css("display","table-row"),$(".board-swimlane-tasks-"+t).css("display","table-row"),$(".hide-icon-swimlane-"+t).css("display","inline"),$(".show-icon-swimlane-"+t).css("display","none")},Kanboard.Swimlane.prototype.collapse=function(t){var e=this.getAllCollapsed();e.indexOf(t)<0&&(e.push(t),localStorage.setItem(this.getStorageKey(),JSON.stringify(e))),$(".board-swimlane-columns-"+t+":not(:first-child)").css("display","none"),$(".board-swimlane-tasks-"+t).css("display","none"),$(".hide-icon-swimlane-"+t).css("display","none"),$(".show-icon-swimlane-"+t).css("display","inline")},Kanboard.Swimlane.prototype.isCollapsed=function(t){return this.getAllCollapsed().indexOf(t)>-1},Kanboard.Swimlane.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]},Kanboard.Swimlane.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".swimlanes-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,o){var n=o.item;n.removeClass("draggable-item-selected"),t.savePosition(n.data("swimlane-id"),n.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Swimlane.prototype.savePosition=function(t,e){var o=$(".swimlanes-table").data("save-position-url"),n=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({swimlane_id:t,position:e}),complete:function(){n.app.hideLoadingIcon()}})},Kanboard.Task=function(t){this.app=t},Kanboard.Task.prototype.onPopoverOpened=function(){var t=this,e=0;t.renderColorPicker(),$(document).on("click",".assign-me",function(t){var e=$(this).data("current-id"),o="#"+$(this).data("target-id");t.preventDefault(),$(o+" option[value="+e+"]").length&&$(o).val(e)}),$(document).on("change","select.task-reload-project-destination",function(){if(e>0)$(this).val(e);else{e=$(this).val();var o=$(this).data("redirect").replace(/PROJECT_ID/g,e);$(".loading-icon").show(),$.ajax({type:"GET",url:o,success:function(o,n,a){e=0,$(".loading-icon").hide(),t.app.get("Popover").ajaxReload(o,a,t.app.get("Popover"))}})}})},Kanboard.Task.prototype.renderColorPicker=function(){function t(t){return $('<div class="color-picker-option"><div class="color-picker-square color-'+t.id+'"></div><div class="color-picker-label">'+t.text+"</div></div>")}$(".color-picker").select2({minimumResultsForSearch:1/0,templateResult:t,templateSelection:t})},Kanboard.Tooltip=function(t){this.app=t},Kanboard.Tooltip.prototype.onBoardRendered=function(){this.execute()},Kanboard.Tooltip.prototype.execute=function(){$(".tooltip").tooltip({track:!1,show:!1,hide:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(t,e){$(this).css(t);var o=e.target.left+e.target.width/2-e.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(e.vertical).addClass(o<1?"align-left":"align-right").appendTo(this)}},content:function(){var t=this,e=$(this).attr("data-href");return e?($.get(e,function(e){var o=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(e),o.css({top:"",left:""}),o.children(".tooltip-arrow").remove();var n=$(t).tooltip("option","position");n.of=$(t),o.position(n)}),'<i class="fa fa-spinner fa-spin"></i>'):'<div class="markdown">'+$(this).attr("title")+"</div>"}}).on("mouseenter",function(){var t=this;$(this).tooltip("open"),$(".ui-tooltip").on("mouseleave",function(){$(t).tooltip("close")})}).on("mouseleave focusout",function(t){t.stopImmediatePropagation();var e=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(e).tooltip("close")},100)})},Kanboard.BoardDragAndDrop=function(t){this.app=t,this.savingInProgress=!1},Kanboard.BoardDragAndDrop.prototype.execute=function(){this.app.hasId("board")&&(this.dragAndDrop(),this.executeListeners())},Kanboard.BoardDragAndDrop.prototype.dragAndDrop=function(){var t=this,e=$(".board-task-list"),o={forcePlaceholderSize:!0,tolerance:"pointer",connectWith:".sortable-column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(e,o){var n=o.item,a=n.attr("data-task-id"),i=n.attr("data-position"),r=n.attr("data-column-id"),s=n.attr("data-swimlane-id"),d=n.parent().attr("data-column-id"),l=n.parent().attr("data-swimlane-id"),c=n.index()+1;n.removeClass("draggable-item-selected"),d==r&&l==s&&c==i||(t.changeTaskState(a),t.save(a,r,d,c,l))},start:function(t,e){e.item.addClass("draggable-item-selected"),e.placeholder.height(e.item.height())}};isMobile.any&&($(".task-board-sort-handle").css("display","inline"),o.handle=".task-board-sort-handle"),e.each(function(){$(this).css("min-height",$(this).parent().height())}),e.sortable(o)},Kanboard.BoardDragAndDrop.prototype.changeTaskState=function(t){var e=$("div[data-task-id="+t+"]");e.addClass("task-board-saving-state"),e.find(".task-board-saving-icon").show()},Kanboard.BoardDragAndDrop.prototype.save=function(t,e,o,n,a){var i=this;i.app.showLoadingIcon(),i.savingInProgress=!0,$.ajax({cache:!1,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t,src_column_id:e,dst_column_id:o,swimlane_id:a,position:n}),success:function(t){i.refresh(t),i.savingInProgress=!1},error:function(){i.app.hideLoadingIcon(),i.savingInProgress=!1; -},statusCode:{403:function(t){window.alert(t.responseJSON.message),document.location.reload(!0)}}})},Kanboard.BoardDragAndDrop.prototype.refresh=function(t){$("#board-container").replaceWith(t),this.app.hideLoadingIcon(),this.dragAndDrop(),this.executeListeners()},Kanboard.BoardDragAndDrop.prototype.executeListeners=function(){for(var t in this.app.controllers){var e=this.app.get(t);"function"==typeof e.onBoardRendered&&e.onBoardRendered()}};var _KB=null;jQuery(document).ready(function(){_KB=new Kanboard.App,_KB.execute()});
\ No newline at end of file +!function(){function t(t,a,i){if(!n)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var r=i&&i.debug||!1;if(r){var d=document.querySelector("#input-textarea-caret-position-mirror-div");d&&d.parentNode.removeChild(d)}var s=document.createElement("div");s.id="input-textarea-caret-position-mirror-div",document.body.appendChild(s);var l=s.style,c=window.getComputedStyle?getComputedStyle(t):t.currentStyle;l.whiteSpace="pre-wrap","INPUT"!==t.nodeName&&(l.wordWrap="break-word"),l.position="absolute",r||(l.visibility="hidden"),e.forEach(function(t){l[t]=c[t]}),o?t.scrollHeight>parseInt(c.height)&&(l.overflowY="scroll"):l.overflow="hidden",s.textContent=t.value.substring(0,a),"INPUT"===t.nodeName&&(s.textContent=s.textContent.replace(/\s/g,"Â "));var u=document.createElement("span");u.textContent=t.value.substring(a)||".",s.appendChild(u);var p={top:u.offsetTop+parseInt(c.borderTopWidth),left:u.offsetLeft+parseInt(c.borderLeftWidth)};return r?u.style.backgroundColor="#aaa":document.body.removeChild(s),p}var e=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],n="undefined"!=typeof window,o=n&&null!=window.mozInnerScreenX;"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=t:n&&(window.getCaretCoordinates=t)}(),function(){function t(){if(!("KeyboardEvent"in window)||"key"in KeyboardEvent.prototype)return!1;var t={get:function(t){var e=n.keys[this.which||this.keyCode];return Array.isArray(e)&&(e=e[+this.shiftKey]),e}};return Object.defineProperty(KeyboardEvent.prototype,"key",t),t}var e,n={polyfill:t,keys:{3:"Cancel",6:"Help",8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",28:"Convert",29:"NonConvert",30:"Accept",31:"ModeChange",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",41:"Select",42:"Print",43:"Execute",44:"PrintScreen",45:"Insert",46:"Delete",48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],91:"OS",93:"ContextMenu",144:"NumLock",145:"ScrollLock",181:"VolumeMute",182:"VolumeDown",183:"VolumeUp",186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"'],224:"Meta",225:"AltGraph",246:"Attn",247:"CrSel",248:"ExSel",249:"EraseEof",250:"Play",251:"ZoomOut"}};for(e=1;e<25;e++)n.keys[111+e]="F"+e;var o="";for(e=65;e<91;e++)o=String.fromCharCode(e),n.keys[e]=[o.toLowerCase(),o.toUpperCase()];n.polyfill()}(),Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(t){for(var e=(this.document||this.ownerDocument).querySelectorAll(t),n=e.length;--n>=0&&e.item(n)!==this;);return n>-1});var KB={components:{},utils:{},html:{},http:{},listeners:{clicks:{},changes:{},keys:{},internals:{}}};KB.on=function(t,e){this.listeners.internals.hasOwnProperty(t)||(this.listeners.internals[t]=[]),this.listeners.internals[t].push(e)},KB.trigger=function(t,e){if(this.listeners.internals.hasOwnProperty(t))for(var n=0;n<this.listeners.internals[t].length;n++)this.listeners.internals[t][n](e)},KB.onClick=function(t,e){this.listeners.clicks[t]=e},KB.onChange=function(t,e){this.listeners.changes[t]=e},KB.onKey=function(t,e,n){this.listeners.keys[t]={callback:e,ignoreInputField:n||!1}},KB.listen=function(){function t(t){for(var e in o.listeners.clicks)o.listeners.clicks.hasOwnProperty(e)&&t.target.matches(e)&&(t.preventDefault(),o.listeners.clicks[e](t))}function e(t){for(var e in o.listeners.changes)o.listeners.changes.hasOwnProperty(e)&&t.target.matches(e)&&o.listeners.changes[e](t.target)}function n(t){var e=KB.utils.getKey(t),n=KB.utils.isInputField(t);if(n&&["Escape","Meta","Enter","Control"].indexOf(e)===-1||a.push(e),a.length>0){var i=!0;for(var r in o.listeners.keys)if(o.listeners.keys.hasOwnProperty(r)){var d=o.listeners.keys[r],s=r.split("+");if(KB.utils.arraysIdentical(a,s)){if(n&&!d.ignoreInputField)return void(a=[]);t.preventDefault(),t.stopPropagation(),a=[],d.callback(t);break}KB.utils.arraysStartsWith(a,s)&&(i=!1)}i&&(a=[])}}var o=this,a=[];document.addEventListener("click",t,!1),document.addEventListener("change",e,!1),window.addEventListener("keydown",n,!1)},KB.component=function(t,e){this.components[t]=e},KB.getComponent=function(t,e,n){var o=this.components[t];return new o(e,n)},KB.render=function(){for(var t in this.components)for(var e=document.querySelectorAll(".js-"+t),n=0;n<e.length;n++)if(this.components.hasOwnProperty(t)){var o;e[n].dataset.params&&(o=JSON.parse(e[n].dataset.params));var a=KB.getComponent(t,e[n],o);a.render(),e[n].className=e[n].className+"-rendered"}},KB.interval=function(t,e){setInterval(e,1e3*t)},KB.dom=function(t){function e(t){var e="string"==typeof t?document.createElement(t):t;this.attr=function(t,n){return null!==n&&"undefined"!=typeof n?(e.setAttribute(t,n),this):e.getAttribute(t)},this.data=function(t,n){return 1===arguments.length?e.dataset[t]:(e.dataset[t]=n,this)},this.hide=function(){return e.style.display="none",this},this.show=function(){return e.style.display="block",this},this.toggle=function(){return"none"===e.style.display?this.show():this.hide(),this},this.style=function(t,n){return e.style[t]=n,this},this.on=function(t,n,o){return e.addEventListener(t,function(t){o||t.preventDefault(),n(t.target)}),this},this.click=function(t){return this.on("click",t)},this.mouseover=function(t){return this.on("mouseover",t)},this.change=function(t){return this.on("change",t)},this.add=function(t){return e.appendChild(t),this},this.replace=function(t){return e.parentNode.replaceChild(t,e),this},this.html=function(t){return e.innerHTML=t,this},this.text=function(t){return e.appendChild(document.createTextNode(t)),this},this.replaceText=function(t){return e.textContent=t,this},this.addClass=function(t){return e.classList.add(t),this},this.removeClass=function(t){return e.classList.remove(t),this},this.toggleClass=function(t){return e.classList.toggle(t),this},this.hasClass=function(t){return e.classList.contains(t)},this.disable=function(){return e.disabled=!0,this},this.enable=function(){return e.disabled=!1,this},this.remove=function(){return e.parentNode.removeChild(e),this},this.empty=function(){for(;e.firstChild;)e.removeChild(e.firstChild);return this},this.parent=function(t){for(;e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null},this.find=function(t){return e.querySelector(t)},this["for"]=function(t,n){for(var o=0;o<n.length;o++){var a=n[o];if("object"!=typeof a)e.appendChild(KB.dom(t).text(a).build());else{var i=KB.dom(t);for(var r in a)a.hasOwnProperty(r)&&r in this&&"function"==typeof this[r]?i[r](a[r]):i.attr(r,a[r]);e.appendChild(i.build())}}return this},this.build=function(){return e}}return new e(t)},KB.find=function(t){var e=document.querySelector(t);return e?KB.dom(e):null},KB.exists=function(t){return!!document.querySelector(t)},KB.focus=function(t){var e=document.querySelector(t);if(e)return e.focus()},KB.html.label=function(t,e){return KB.dom("label").attr("for",e).text(t).build()},KB.html.radio=function(t,e,n){return KB.dom("label").add(KB.dom("input").attr("type","radio").attr("name",e).attr("value",n).build()).text(t).build()},KB.html.radios=function(t){var e=KB.dom("div");for(var n in t)t.hasOwnProperty(n)&&e.add(KB.html.radio(n.label,n.name,n.value))},KB.http.request=function(t,e,n,o){function a(t){var e=t.getResponseHeader("X-Ajax-Redirect");if("self"===e)window.location.reload();else if(e&&e.indexOf("#")>-1)window.location=e.split("#")[0];else if(e)window.location=e;else if("application/json"===t.getResponseHeader("Content-Type"))try{return JSON.parse(t.responseText)}catch(n){}return t.responseText}var i=function(){},r=function(){};this.execute=function(){var d=new XMLHttpRequest;d.open(t,e,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest");for(var s in n)n.hasOwnProperty(s)&&d.setRequestHeader(s,n[s]);return d.onerror=function(){r()},d.onreadystatechange=function(){if(d.readyState===XMLHttpRequest.DONE){var t=a(d);200===d.status?i(t):r(t)}},d.send(o),this},this.success=function(t){return i=t,this},this.error=function(t){return r=t,this}},KB.http.get=function(t){return new KB.http.request("GET",t).execute()},KB.http.postJson=function(t,e){var n={"Content-Type":"application/json",Accept:"application/json"};return new KB.http.request("POST",t,n,JSON.stringify(e)).execute()},KB.http.postForm=function(t,e){var n=new FormData(e);return new KB.http.request("POST",t,{},n).execute()},KB.http.uploadFile=function(t,e,n,o,a){var i=new FormData;i.append("files[]",e);var r=new XMLHttpRequest;r.upload.addEventListener("progress",n),r.upload.addEventListener("load",o),r.upload.addEventListener("error",a),r.open("POST",t,!0),r.send(i)},function(){function t(t){t.target.matches("#modal-overlay")&&(t.stopPropagation(),t.preventDefault(),s())}function e(){s()}function n(){KB.trigger("modal.loading"),a()}function o(){return document.querySelector("#modal-content form")}function a(){var t=o();if(t){var e=t.getAttribute("action");e&&KB.http.postForm(e,t).success(function(t){KB.trigger("modal.stop"),t?r(t):s()})}}function i(){var t=KB.find("#modal-content form");t&&t.on("submit",n,!1);var e=document.querySelector("#modal-content input[autofocus]");e&&e.focus(),KB.render(),_KB.datePicker(),_KB.autoComplete(),_KB.tagAutoComplete(),_KB.get("Task").onPopoverOpened()}function r(t){var e=KB.find("#modal-content");e&&(e.replace(KB.dom("div").attr("id","modal-content").html(t).build()),i())}function d(n,o,a){var r=KB.dom("a").attr("href","#").attr("id","modal-close-button").html('<i class="fa fa-times"></i>').click(e).build(),d=KB.dom("div").attr("id","modal-header").add(r).build(),s=KB.dom("div").attr("id","modal-content").html(n).build(),l=KB.dom("div").attr("id","modal-box").style("width",o).add(d).add(s).build(),c=KB.dom("div").attr("id","modal-overlay").add(l).build();a&&c.addEventListener("click",t,!1),document.body.appendChild(c),i()}function s(){var t=KB.find("#modal-overlay");t&&t.remove()}function l(t){var e=KB.utils.getViewportSize();switch(t){case"large":return e.width<1300?"95%":"1300px";case"medium":return e.width<1024?"70%":"1024px"}return e.width<800?"75%":"800px"}var c=!1;KB.on("modal.close",function(){s()}),KB.on("modal.submit",function(){a()}),KB.modal={open:function(t,e,n){_KB.get("Dropdown").close(),s(),"undefined"==typeof n&&(n=!0),KB.http.get(t).success(function(t){c=!0,d(t,l(e),n)})},close:function(){c=!1,s()},isOpen:function(){return c},replace:function(t){KB.http.get(t).success(function(t){r(t)})},getForm:o}}(),KB.utils.formatDuration=function(t){return t>=86400?Math.round(t/86400)+"d":t>=3600?Math.round(t/3600)+"h":t>=60?Math.round(t/60)+"m":t+"s"},KB.utils.getSelectionPosition=function(t){var e,n;return e=t.value.length<t.selectionStart?t.value.length:t.selectionStart,n=t.selectionStart===t.selectionEnd?e:t.selectionEnd,{selectionStart:e,selectionEnd:n}},KB.utils.arraysIdentical=function(t,e){var n=t.length;if(n!==e.length)return!1;for(;n--;)if(t[n]!==e[n])return!1;return!0},KB.utils.arraysStartsWith=function(t,e){for(var n=Math.min(t.length,e.length),o=0;o<n;o++)if(t[o]!==e[o])return!1;return!0},KB.utils.isInputField=function(t){var e=t.target;return!("INPUT"!==e.tagName&&"SELECT"!==e.tagName&&"TEXTAREA"!==e.tagName&&!e.isContentEditable)},KB.utils.getKey=function(t){var e={Esc:"Escape",Up:"ArrowUp",Down:"ArrowDown",Left:"ArrowLeft",Right:"ArrowRight"};for(var n in e)if(e.hasOwnProperty(n)&&n===t.key)return e[n];return t.key},KB.utils.getViewportSize=function(){return{width:Math.max(document.documentElement.clientWidth,window.innerWidth||0),height:Math.max(document.documentElement.clientHeight,window.innerHeight||0)}},KB.onClick(".accordion-toggle",function(t){var e=KB.dom(t.target).parent(".accordion-section");e&&(KB.dom(e).toggleClass("accordion-collapsed"),KB.dom(KB.dom(e).find(".accordion-content")).toggle())}),KB.component("calendar",function(t,e){this.render=function(){var n=$(t);n.fullCalendar({locale:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(t){$.ajax({cache:!1,url:e.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t.id,date_due:t.start.format()})})},viewRender:function(){var t=e.checkUrl,o={start:n.fullCalendar("getView").start.format(),end:n.fullCalendar("getView").end.format()};for(var a in o)t+="&"+a+"="+o[a];$.getJSON(t,function(t){n.fullCalendar("removeEvents"),n.fullCalendar("addEventSource",t),n.fullCalendar("rerenderEvents")})}})}}),KB.component("chart-project-avg-time-column",function(t,e){this.render=function(){var n=e.metrics,o=[e.label],a=[];for(var i in n)o.push(n[i].average),a.push(n[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("chart-project-burndown",function(t,e){this.render=function(){for(var n=e.metrics,o=[[e.labelTotal]],a=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(e.dateFormat),d=0;d<n.length;d++)for(var s=0;s<n[d].length;s++)0===d?o.push([n[d][s]]):(o[s+1].push(n[d][s]),s>0&&(void 0===o[0][d]&&o[0].push(0),o[0][d]+=n[d][s]),0===s&&a.push(r(i.parse(n[d][s]))));KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o},axis:{x:{type:"category",categories:a}}})}}),KB.component("chart-project-cumulative-flow",function(t,e){this.render=function(){for(var n=e.metrics,o=[],a=[],i=[],r=d3.time.format("%Y-%m-%d"),d=d3.time.format(e.dateFormat),s=0;s<n.length;s++)for(var l=0;l<n[s].length;l++)0===s?(o.push([n[s][l]]),l>0&&a.push(n[s][l])):(o[l].push(n[s][l]),0===l&&i.push(d(r.parse(n[s][l]))));KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:o,type:"area-spline",groups:[a]},axis:{x:{type:"category",categories:i}}})}}),KB.component("chart-project-lead-cycle-time",function(t,e){this.render=function(){var n=e.metrics,o=[e.labelCycle],a=[e.labelLead],i=[],r={};r[e.labelCycle]="area",r[e.labelLead]="area-spline";var d={};d[e.labelLead]="#afb42b",d[e.labelCycle]="#4e342e";for(var s=0;s<n.length;s++)o.push(parseInt(n[s].avg_cycle_time)),a.push(parseInt(n[s].avg_lead_time)),i.push(n[s].day);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[a,o],types:r,colors:d},axis:{x:{type:"category",categories:i},y:{tick:{format:KB.utils.formatDuration}}}})}}),KB.component("chart-project-task-distribution",function(t,e){this.render=function(){for(var n=[],o=0;o<e.metrics.length;o++)n.push([e.metrics[o].column_title,e.metrics[o].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n,type:"donut"}})}}),KB.component("chart-project-time-comparison",function(t,e){this.render=function(){var n=[e.labelSpent],o=[e.labelEstimated],a=[];for(var i in e.metrics)n.push(e.metrics[i].time_spent),o.push(e.metrics[i].time_estimated),a.push("open"===i?e.labelOpen:e.labelClosed);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[n,o],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:a}},legend:{show:!0}})}}),KB.component("chart-project-user-distribution",function(t,e){this.render=function(){for(var n=[],o=0;o<e.metrics.length;o++)n.push([e.metrics[o].user,e.metrics[o].nb_tasks]);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:n,type:"donut"}})}}),KB.component("chart-task-time-column",function(t,e){this.render=function(){for(var n=e.metrics,o=[e.label],a=[],i=0;i<n.length;i++)o.push(n[i].time_spent),a.push(n[i].title);KB.dom(t).add(KB.dom("div").attr("id","chart").build()),c3.generate({data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:KB.utils.formatDuration}}},legend:{show:!1}})}}),KB.component("confirm-buttons",function(t,e){function n(){r=!0,KB.find("#modal-confirm-button").replace(i()),window.location.href=e.url}function o(){KB.trigger("modal.close")}function a(){r=!1,KB.find("#modal-confirm-button").replace(i())}function i(){var t=KB.dom("button").click(n).attr("id","modal-confirm-button").attr("type","button").attr("class","btn btn-red");return r&&t.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),e.tabindex&&t.attr("tabindex",e.tabindex),t.text(e.submitLabel).build()}var r=!1;this.render=function(){KB.on("modal.stop",a);var n=KB.dom("div").attr("class","form-actions").add(i()).text(" "+e.orLabel+" ").add(KB.dom("a").attr("href","#").click(o).text(e.cancelLabel).build()).build();t.appendChild(n)}}),KB.component("external-task-view",function(t,e){this.render=function(){$.ajax({cache:!1,url:e.url,success:function(e){KB.dom(t).html('<div id="external-task-view">'+e+"</div>")}})}}),KB.component("file-upload",function(t,e){function n(t){if(t.lengthComputable){var e=t.loaded/t.total,n=Math.floor(100*e);KB.find("#file-progress-"+w).attr("value",e),KB.find("#file-percentage-"+w).replaceText("("+n+"%)")}}function o(){var t=KB.dom("div").addClass("file-error").text(e.labelUploadError).build();KB.find("#file-item-"+w).add(t)}function a(){if(w++,w<y.length)KB.http.uploadFile(e.url,y[w],n,a,o);else{KB.trigger("modal.stop"),KB.trigger("modal.hide");var t=KB.dom("div").addClass("alert").addClass("alert-success").text(e.labelSuccess).build(),r=KB.dom("button").attr("type","button").addClass("btn").addClass("btn-blue").click(i).text(e.labelCloseSuccess).build();KB.dom(K).replace(KB.dom("div").add(t).add(r).build())}}function i(){window.location.reload()}function r(){w=0,u()}function d(){y=b.files,p()}function s(){y=[],w=0,b.click()}function l(t){t.stopPropagation(),t.preventDefault()}function c(t){t.stopPropagation(),t.preventDefault(),y=t.dataTransfer.files,p()}function u(){y.length>0&&KB.http.uploadFile(e.url,y[w],n,a,o)}function p(){y.length>0?(KB.trigger("modal.enable"),KB.dom(K).empty().add(v())):(KB.trigger("modal.disable"),KB.dom(K).empty().add(h()))}function f(){return KB.dom("input").attr("id","file-input-element").attr("type","file").attr("name","files[]").attr("multiple",!0).on("change",d).hide().build()}function h(){var t=KB.dom("a").attr("href","#").text(e.labelChooseFiles).click(s).build();return KB.dom("div").attr("id","file-dropzone-inner").text(e.labelDropzone+" "+e.labelOr+" ").add(t).build()}function m(){var t=KB.dom("div").attr("id","file-dropzone").add(h()).build();return t.ondragover=l,t.ondrop=c,t.ondragover=l,t}function g(t){var n=!1,o=KB.dom("progress").attr("id","file-progress-"+t).attr("value",0).build(),a=KB.dom("span").attr("id","file-percentage-"+t).text("(0%)").build(),i=KB.dom("li").attr("id","file-item-"+t).add(o).text(" "+y[t].name+" ").add(a);return y[t].size>e.maxSize&&(i.add(KB.dom("div").addClass("file-error").text(e.labelOversize).build()),n=!0),n&&KB.trigger("modal.disable"),i.build()}function v(){for(var t=KB.dom("ul").attr("id","file-list").build(),e=0;e<y.length;e++)t.appendChild(g(e));return t}var b=null,K=null,y=[],w=null;this.render=function(){KB.on("modal.submit",r),b=f(),K=m(),t.appendChild(b),t.appendChild(K)}}),KB.component("image-slideshow",function(t,e){function n(t){switch(KB.utils.getKey(t)){case"Escape":s();break;case"ArrowRight":i();break;case"ArrowLeft":r()}}function o(t){t.matches(".slideshow-next-icon")?i():t.matches(".slideshow-previous-icon")?r():t.matches(".slideshow-download-icon")?window.location.href=t.href:s()}function a(t){var e=KB.dom(t).data("imageId"),n=l(e);d(n)}function i(){s();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var n=t+1;n>=e.images.length&&(n=0),p=e.images[n];break}d()}function r(){s();for(var t=0;t<e.images.length;t++)if(e.images[t].id===p.id){var n=t-1;n<0&&(n=e.images.length-1),p=e.images[n];break}d()}function d(){var t=KB.dom("div").attr("class","fa fa-window-close slideshow-icon slideshow-close-icon").build(),e=KB.dom("a").attr("class","fa fa-download slideshow-icon slideshow-download-icon").attr("href",c(p,"download")).build(),a=KB.dom("div").attr("class","fa fa-chevron-circle-left slideshow-icon slideshow-previous-icon").build(),i=KB.dom("div").attr("class","fa fa-chevron-circle-right slideshow-icon slideshow-next-icon").build(),r=KB.dom("img").attr("src",c(p,"image")).attr("alt",p.name).attr("title",p.name).style("maxHeight",window.innerHeight-50+"px").build(),d=KB.dom("figcaption").text(p.name).build(),s=KB.dom("figure").add(r).add(d).build(),l=KB.dom("div").addClass("image-slideshow-overlay").add(t).add(e).add(a).add(i).add(s).click(o).build();document.body.appendChild(l),document.addEventListener("keydown",n,!1)}function s(){var t=KB.find(".image-slideshow-overlay");null!==t&&(document.removeEventListener("keydown",n,!1),t.remove())}function l(t){for(var n=0;n<e.images.length;n++)if(e.images[n].id===t)return e.images[n];return null}function c(t,n){var o=new RegExp(e.regex,"g");return e.url[n].replace(o,t.id)}function u(t){return KB.dom("img").attr("src",c(t,"thumbnail")).attr("alt",t.name).attr("title",t.name).data("imageId",t.id).click(a).build()}var p;this.render=function(){p=e.image,t.appendChild(u(p))}}),KB.keyboardShortcuts=function(){function t(t){var e=KB.find(t);null!==e&&(window.location=e.attr("href"))}function e(){var t=$("form");1==t.length?t.submit():t.length>1&&("INPUT"===document.activeElement.tagName||"TEXTAREA"===document.activeElement.tagName?$(document.activeElement).parents("form").submit():KB.modal.isOpen()&&KB.modal.getForm().submit())}KB.onKey("?",function(){KB.modal.open(KB.find("body").data("keyboardShortcutUrl"))}),KB.onKey("Escape",function(){KB.exists("#suggest-menu")||(KB.trigger("modal.close"),_KB.get("Dropdown").close())}),KB.onKey("Meta+Enter",e,!0),KB.onKey("Control+Enter",e,!0),KB.onKey("b",function(){KB.trigger("board.selector.open")}),KB.exists("#board")&&(KB.onKey("c",function(){_KB.get("BoardHorizontalScrolling").toggle()}),KB.onKey("s",function(){_KB.get("BoardCollapsedMode").toggle()}),KB.onKey("n",function(){KB.modal.open(KB.find("#board").data("taskCreationUrl"),"large",!1)})),KB.exists("#task-view")&&(KB.onKey("e",function(){KB.modal.open(KB.find("#task-view").data("editUrl"),"large")}),KB.onKey("c",function(){KB.modal.open(KB.find("#task-view").data("commentUrl"))}),KB.onKey("s",function(){KB.modal.open(KB.find("#task-view").data("subtaskUrl"))}),KB.onKey("l",function(){KB.modal.open(KB.find("#task-view").data("internalLinkUrl"))})),KB.onKey("f",function(){KB.focus("#form-search")}),KB.onKey("r",function(){var t=$(".filter-reset").data("filter"),e=$("#form-search");e.val(t),$("form.search").submit()}),KB.onKey("v+o",function(){t("a.view-overview")}),KB.onKey("v+b",function(){t("a.view-board")}),KB.onKey("v+c",function(){t("a.view-calendar")}),KB.onKey("v+l",function(){t("a.view-listing")}),KB.onKey("v+g",function(){t("a.view-gantt")})},function(){function t(t){return"I"===t.target.tagName?t.target.parentNode.getAttribute("href"):t.target.getAttribute("href")}KB.onClick(".js-modal-large",function(e){KB.modal.open(t(e),"large",!1)}),KB.onClick(".js-modal-medium",function(e){KB.modal.open(t(e),"medium",!1)}),KB.onClick(".js-modal-small",function(e){KB.modal.open(t(e),"small",!1)}),KB.onClick(".js-modal-confirm",function(e){KB.modal.open(t(e),"small")}),KB.onClick(".js-modal-close",function(){KB.modal.close()})}(),KB.onChange(".js-project-creation-select-options",function(t){var e=t.value;"0"===e?KB.find(".js-project-creation-options").hide():KB.find(".js-project-creation-options").show()}),KB.component("project-select-role",function(t,e){function n(t){d=!0,e.role=t.value,a(),o()}function o(){KB.http.postJson(e.url,{id:e.id,role:e.role}).success(function(){d=!1,s=!0,a()}).error(function(){d=!1,s=!1,l=!0,a()})}function a(){KB.dom(r).remove(),r=i(),t.appendChild(r)}function i(){var t=[],o=KB.dom("div");for(var a in e.roles)if(e.roles.hasOwnProperty(a)){var i={value:a,text:e.roles[a]};e.role===a&&(i.selected="selected"),t.push(i)}return o.add(KB.dom("select").change(n)["for"]("option",t).build()),d?(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-spinner fa-pulse fa-fw").build())):s?(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-success").build())):l&&(o.text(" "),o.add(KB.dom("i").attr("class","fa fa-check fa-fw icon-fade-out icon-error").build())),o.build()}var r,d=!1,s=!1,l=!1;this.render=function(){r=i(),t.appendChild(r)}}),KB.component("screenshot",function(t){function e(t){if(t.clipboardData&&t.clipboardData.items){var e=t.clipboardData.items;if(e)for(var n=0;n<e.length;n++)if(e[n].type.indexOf("image")!==-1){var o=e[n].getAsFile(),a=new FileReader;a.onload=function(t){r(t.target.result)},a.readAsDataURL(o)}}else setTimeout(i,100)}function n(){o(),window.Clipboard||(d=document.createElement("div"),d.id="screenshot-pastezone",d.contentEditable=!0,d.style.opacity=0,d.style.position="fixed",d.style.top=0,d.style.right=0,d.style.width=0,document.body.insertBefore(d,document.body.firstChild),d.focus(),document.addEventListener("click",a),document.getElementById("screenshot-zone").addEventListener("click",a)),window.addEventListener("paste",e,!1)}function o(){null!==d?document.body.removeChild(d):document.getElementById("screenshot-pastezone")&&document.body.removeChild(document.getElementById("screenshot-pastezone")),document.removeEventListener("click",a),d=null}function a(){null!==d&&d.focus()}function i(){var t=d.childNodes[0];t&&"IMG"===t.tagName&&r(t.src),d.innerHTML=""}function r(t){var e=new Image;e.src=t,e.onload=function(){var e=t.split("base64,");s.value=e[1]};var a=document.getElementById("screenshot-zone");a.innerHTML="",a.className="screenshot-pasted",a.appendChild(e),o(),n()}var d=null,s=null;this.render=function(){s=KB.dom("input").attr("type","hidden").attr("name","screenshot").build(),t.appendChild(s),n()}}),KB.component("select-dropdown-autocomplete",function(t,e){function n(){KB.dom(x).show(),KB.dom(C).hide()}function o(){KB.dom(x).hide(),KB.dom(C).show()}function a(){var t=KB.find("#select-dropdown-menu");if(t){var e=w.getBoundingClientRect();t.style("top",document.body.scrollTop+e.bottom+"px")}}function i(t){switch(KB.utils.getKey(t)){case"Escape":B.value="",b();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),f();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),h();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(),u()}}function r(){b(),K()}function d(t){KB.dom(t).hasClass("select-dropdown-menu-item")&&(KB.find(".select-dropdown-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function s(){u()}function l(e){t.contains(e.target)||(B.value="",b())}function c(){var t=KB.find("#select-dropdown-menu");null===t?K():b()}function u(){var t=KB.find(".select-dropdown-menu-item.active"),o=t.data("value");k.value=o,B.value=e.items[o],b(),e.redirect?window.location=e.redirect.url.replace(new RegExp(e.redirect.regex,"g"),o):e.replace&&(n(),KB.modal.replace(e.replace.url.replace(new RegExp(e.replace.regex,"g"),o)))}function p(){for(var t=document.querySelectorAll(".select-dropdown-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function f(){var t=p();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function h(){var t=p();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function m(t){var n=[],o=Object.keys(t);e.sortByKeys&&o.sort();for(var a=0;a<o.length;a++)n.push({"class":"select-dropdown-menu-item",text:t[o[a]],"data-label":t[o[a]],"data-value":o[a]});return n}function g(t,n){for(var o=[],a=!1,i=0;i<n.length;i++)if(0===t.length||0===n[i]["data-label"].toLowerCase().indexOf(t.toLowerCase())){var r=n[i];"undefined"!=typeof e.defaultValue&&String(e.defaultValue)===r["data-value"]&&(r["class"]+=" active",a=!0),o.push(r)}return!a&&o.length>0&&(o[0]["class"]+=" active"),o}function v(){var t=g(B.value,m(e.items)),n=w.getBoundingClientRect();return 0===t.length?null:KB.dom("ul").attr("id","select-dropdown-menu").style("top",document.body.scrollTop+n.bottom+"px").style("left",n.left+"px").style("width",n.width+"px").style("maxHeight",window.innerHeight-n.bottom-20+"px").mouseover(d).click(s)["for"]("li",t).build()}function b(){var t=KB.find("#select-dropdown-menu");null!==t&&t.remove(),document.removeEventListener("keydown",i,!1),document.removeEventListener("click",l,!1)}function K(){var t=v();null!==t&&document.body.appendChild(t),document.addEventListener("keydown",i,!1),document.addEventListener("click",l,!1)}function y(){return e.defaultValue&&e.defaultValue in e.items?e.items[e.defaultValue]:e.placeholder?e.placeholder:""}var w,B,k,C,x;this.render=function(){KB.on("select.dropdown.loading.start",n),KB.on("select.dropdown.loading.stop",o),C=KB.dom("i").attr("class","fa fa-chevron-down select-dropdown-chevron").click(c).build(),x=KB.dom("span").hide().addClass("select-loading-icon").add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).build(),k=KB.dom("input").attr("type","hidden").attr("name",e.name).attr("value",e.defaultValue||"").build(),B=KB.dom("input").attr("type","text").attr("placeholder",y()).addClass("select-dropdown-input").style("width",t.offsetWidth-30+"px").on("focus",c).on("input",r,!0).build(),w=KB.dom("div").addClass("select-dropdown-input-container").add(k).add(B).add(C).add(x).build(),t.appendChild(w),e.onFocus&&e.onFocus.forEach(function(t){KB.on(t,function(){B.focus()})}),window.addEventListener("scroll",a,!1)}}),KB.interval(60,function(){var t=KB.find("body").data("statusUrl"),e=KB.find("body").data("loginUrl");null===KB.find(".form-login")&&KB.http.get(t).error(function(){window.location=e})}),KB.component("submit-buttons",function(t,e){function n(){c=!0,KB.find("#modal-submit-button").replace(l()),KB.trigger("modal.submit")}function o(){KB.trigger("modal.close")}function a(){c=!1,KB.find("#modal-submit-button").replace(l())}function i(){c=!1,u=!0,KB.find("#modal-submit-button").replace(l())}function r(){c=!1,u=!1,KB.find("#modal-submit-button").replace(l())}function d(){KB.dom(f).hide()}function s(t){p=t.submitLabel,KB.find("#modal-submit-button").replace(l())}function l(){var t=KB.dom("button").attr("id","modal-submit-button").attr("type","submit").attr("class","btn btn-"+(e.color||"blue"));return KB.modal.isOpen()&&t.click(n),e.tabindex&&t.attr("tabindex",e.tabindex),c&&t.disable().add(KB.dom("i").attr("class","fa fa-spinner fa-pulse").build()).text(" "),u&&t.disable(),t.text(p).build()}var c=!1,u=e.disabled||!1,p=e.submitLabel,f=null;this.render=function(){KB.on("modal.stop",a),KB.on("modal.disable",i),KB.on("modal.enable",r),KB.on("modal.hide",d),KB.on("modal.submit.label",s);var n=KB.dom("div").attr("class","form-actions").add(l());KB.modal.isOpen()&&n.text(" "+e.orLabel+" ").add(KB.dom("a").attr("href","#").click(o).text(e.cancelLabel).build()),f=n.build(),t.appendChild(f)}}),KB.component("suggest-menu",function(t,e){function n(t){switch(KB.utils.getKey(t)){case"Escape":u();break;case"ArrowUp":t.preventDefault(),t.stopImmediatePropagation(),l();break;case"ArrowDown":t.preventDefault(),t.stopImmediatePropagation(),c();break;case"Enter":t.preventDefault(),t.stopImmediatePropagation(), +i()}}function o(){i()}function a(t){KB.dom(t).hasClass("suggest-menu-item")&&(KB.find(".suggest-menu-item.active").removeClass("active"),KB.dom(t).addClass("active"))}function i(){t.focus();var e=KB.find(".suggest-menu-item.active"),n=e.data("value"),o=e.data("trigger"),a=t.value,i=r(t),d=o+n+" ",s=KB.utils.getSelectionPosition(t),l=a.substring(0,s.selectionStart-i.length),c=a.substring(s.selectionEnd),p=l.length+d.length;t.value=l+d+c,t.setSelectionRange(p,p),u()}function r(t){var e=t.value.substring(0,t.selectionEnd).split("\n"),n=e[e.length-1],o=n.split(" ");return o[o.length-1]}function d(){for(var t=["#modal-content form","#modal-content","body"],e=0;e<t.length;e++){var n=document.querySelector(t[e]);if(null!==n)return n}return null}function s(){for(var t=document.querySelectorAll(".suggest-menu-item"),e=0;e<t.length;e++)if(KB.dom(t[e]).hasClass("active")){KB.dom(t[e]).removeClass("active");break}return{items:t,index:e}}function l(){var t=s();t.index>0&&(t.index=t.index-1),KB.dom(t.items[t.index]).addClass("active")}function c(){var t=s();t.index<t.items.length-1&&t.index++,KB.dom(t.items[t.index]).addClass("active")}function u(){var t=KB.find("#suggest-menu");null!==t&&t.remove(),document.removeEventListener("keydown",n,!1)}function p(t){var n=r(t),o=f(n,e.triggers);u(),null!==o&&h(o,n.substring(o.length),e.triggers[o])}function f(t,e){for(var n in e)if(e.hasOwnProperty(n)&&0===t.indexOf(n))return n;return null}function h(t,e,n){if("string"==typeof n){var o=new RegExp("SEARCH_TERM","g"),a=n.replace(o,e);KB.http.get(a).success(function(n){m(t,e,n)})}else m(t,e,n)}function m(t,e,n){n=g(e,n),n.length>0&&b(v(t,n))}function g(t,e){var n=[];if(0===t.length)return e;for(var o=0;o<e.length;o++)0===e[o].value.toLowerCase().indexOf(t.toLowerCase())&&n.push(e[o]);return n}function v(t,e){for(var n=[],o=0;o<e.length;o++){var a="suggest-menu-item";0===o&&(a+=" active"),n.push({"class":a,html:e[o].html,"data-value":e[o].value,"data-trigger":t})}return n}function b(e){var i=d(),r=getCaretCoordinates(t,t.selectionEnd),s=r.left+t.offsetLeft-t.scrollLeft,l=r.top+t.offsetTop-t.scrollTop+16;document.addEventListener("keydown",n,!1);var c=KB.dom("ul").attr("id","suggest-menu").click(o).mouseover(a).style("left",s+"px").style("top",l+"px")["for"]("li",e).build();i.appendChild(c)}this.render=function(){t.addEventListener("input",function(){p(this)})}}),KB.component("task-move-position",function(t,e){function n(t){var e=KB.dom(document).find("#"+t);return e?parseInt(e.options[e.selectedIndex].value):null}function o(){var t=n("form-swimlanes");return null===t?e.board[0].id:t}function a(){var t=n("form-columns");return null===t?e.board[0].columns[0].id:t}function i(){var t=n("form-position");return null===t?1:t}function r(){var t=KB.find("input[name=positionChoice]:checked");return t?t.value:"before"}function d(){var t=KB.dom(document).find("#form-columns");KB.dom(t).replace(p());var e=KB.dom(document).find("#form-tasks");KB.dom(e).replace(f())}function s(){var t=KB.dom(document).find("#form-tasks");KB.dom(t).replace(f())}function l(t){KB.trigger("modal.stop"),KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").attr("class","alert alert-error").text(t).build())}function c(){var t=i(),n=r();"after"===n&&t++,KB.find("#message-container").replace(KB.dom("div").attr("id","message-container").build()),KB.http.postJson(e.saveUrl,{column_id:a(),swimlane_id:o(),position:t}).error(function(t){t&&l(t.message)})}function u(){var t=[];return e.board.forEach(function(e){t.push({value:e.id,text:e.name})}),KB.dom("select").attr("id","form-swimlanes").change(d)["for"]("option",t).build()}function p(){var t=[],n=o();return e.board.forEach(function(e){n===e.id&&e.columns.forEach(function(e){t.push({value:e.id,text:e.title})})}),KB.dom("select").attr("id","form-columns").change(s)["for"]("option",t).build()}function f(){var t=[],n=o(),i=a(),r=KB.dom("div").attr("id","form-tasks");return e.board.forEach(function(e){n===e.id&&e.columns.forEach(function(e){i===e.id&&e.tasks.forEach(function(e){t.push({value:e.position,text:"#"+e.id+" - "+e.title})})})}),t.length>0&&r.add(KB.html.label(e.positionLabel,"form-position")).add(KB.dom("select").attr("id","form-position")["for"]("option",t).build()).add(KB.html.radio(e.beforeLabel,"positionChoice","before")).add(KB.html.radio(e.afterLabel,"positionChoice","after")),r.build()}this.render=function(){KB.on("modal.submit",c);var n=KB.dom("div").add(KB.dom("div").attr("id","message-container").build()).add(KB.html.label(e.swimlaneLabel,"form-swimlanes")).add(u()).add(KB.html.label(e.columnLabel,"form-columns")).add(p()).add(f()).build();t.appendChild(n)}}),KB.component("text-editor",function(t,e){function n(){var t=KB.dom("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:'<i class="fa fa-pencil-square-o fa-fw"></i> '+e.labelWrite,click:function(){a()}}]).build();return m=KB.dom("div").attr("class","text-editor-preview-area markdown").build(),KB.dom("div").attr("class","text-editor-view-mode").add(t).add(m).hide().build()}function o(){var t=KB.dom("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:'<i class="fa fa-eye fa-fw"></i> '+e.labelPreview,click:function(){a()}},{href:"#",html:'<i class="fa fa-bold fa-fw"></i>',click:function(){d("**")}},{href:"#",html:'<i class="fa fa-italic fa-fw"></i>',click:function(){d("_")}},{href:"#",html:'<i class="fa fa-strikethrough fa-fw"></i>',click:function(){d("~~")}},{href:"#",html:'<i class="fa fa-quote-right fa-fw"></i>',click:function(){l("> ")}},{href:"#",html:'<i class="fa fa-list-ul fa-fw"></i>',click:function(){l("* ")}},{href:"#",html:'<i class="fa fa-code fa-fw"></i>',click:function(){s("```")}}]).build(),n=KB.dom("textarea");return n.attr("name",e.name),e.tabindex&&n.attr("tabindex",e.tabindex),e.required&&n.attr("required","required"),n.text(e.text),e.placeholder&&n.attr("placeholder",e.placeholder),p=n.build(),e.suggestOptions&&KB.getComponent("suggest-menu",p,e.suggestOptions).render(),KB.dom("div").attr("class","text-editor-write-mode").add(t).add(p).build()}function a(){KB.dom(m).html(marked(p.value,{sanitize:!0})),KB.dom(f).toggle(),KB.dom(h).toggle()}function i(){return p.value.substring(p.selectionStart,p.selectionEnd)}function r(t,e,n,o){return t.substring(0,e)+o+t.substring(n)}function d(t){var e=i();c(t+e+t),u(t)}function s(t){var e=i();c("\n"+t+"\n"+e+"\n"+t),u(t,2)}function l(t){var e=i();if(e.indexOf("\n")===-1)c("\n"+t+e);else{for(var n=e.split("\n"),o=0;o<n.length;o++)n[o].indexOf(t)===-1&&(n[o]=t+n[o]);c(n.join("\n"))}u(t,1)}function c(t){p.focus();var e=!1,n=KB.utils.getSelectionPosition(p);if(g=n.selectionStart,v=n.selectionEnd,document.queryCommandSupported("insertText")&&(e=document.execCommand("insertText",!1,t)),!e){try{document.execCommand("ms-beginUndoUnit")}catch(o){}p.value=r(p.value,g,v,t);try{document.execCommand("ms-endUndoUnit")}catch(o){}}}function u(t,e){e=e||0;var n=v+t.length+e;p.setSelectionRange(n,n)}var p,f,h,m,g,v;this.render=function(){h=o(),f=n(),t.appendChild(KB.dom("div").attr("class","text-editor").add(f).add(h).build()),e.autofocus&&p.focus()}}),document.addEventListener("DOMContentLoaded",function(){KB.render(),KB.listen(),KB.keyboardShortcuts()});var Kanboard={};Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(t){return this.controllers[t]},Kanboard.App.prototype.execute=function(){for(var t in Kanboard)if("App"!==t){var e=new Kanboard[t](this);this.controllers[t]=e,"function"==typeof e.execute&&e.execute(),"function"==typeof e.listen&&e.listen(),"function"==typeof e.focus&&e.focus()}this.focus(),this.datePicker(),this.autoComplete(),this.tagAutoComplete()},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(t){t.preventDefault()})},Kanboard.App.prototype.datePicker=function(){var t=$("body"),e=t.data("js-date-format"),n=t.data("js-time-format"),o=t.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[o]),$.timepicker.setDefaults($.timepicker.regional[o]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:e,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:e,timeFormat:n,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var t=$(this),e=t.data("dst-field"),n=t.data("dst-extra-field");""===$("#form-"+e).val()&&t.parent().find("button[type=submit]").attr("disabled","disabled"),t.autocomplete({source:t.data("search-url"),minLength:1,select:function(o,a){$("input[name="+e+"]").val(a.item.id),n&&$("input[name="+n+"]").val(a.item[n]),t.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(t){return!!document.getElementById(t)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append('<span id="app-loading-icon"> <i class="fa fa-spinner fa-spin"></i></span>')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.App.prototype.isVisible=function(){var t="";return"undefined"!=typeof document.hidden?t="visibilityState":"undefined"!=typeof document.mozHidden?t="mozVisibilityState":"undefined"!=typeof document.msHidden?t="msVisibilityState":"undefined"!=typeof document.webkitHidden&&(t="webkitVisibilityState"),""===t||"visible"==document[t]},Kanboard.BoardCollapsedMode=function(t){this.app=t},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(e){$(".filter-display-mode").toggle(),t.app.get("BoardDragAndDrop").refresh(e)}})},Kanboard.BoardColumnView=function(t){this.app=t},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var t=this;$(document).on("click",".board-toggle-column-view",function(){t.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var t=this;$(".board-column-header").each(function(){var e=$(this).data("column-id");localStorage.getItem("hidden_column_"+e)&&t.hideColumn(e)})},Kanboard.BoardColumnView.prototype.toggle=function(t){localStorage.getItem("hidden_column_"+t)?this.showColumn(t):this.hideColumn(t)},Kanboard.BoardColumnView.prototype.hideColumn=function(t){$(".board-column-"+t+" .board-column-expanded").hide(),$(".board-column-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t+" .board-column-expanded").hide(),$(".board-column-header-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+t).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+t+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+t).height())}),localStorage.setItem("hidden_column_"+t,1)},Kanboard.BoardColumnView.prototype.showColumn=function(t){$(".board-column-"+t+" .board-column-expanded").show(),$(".board-column-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t+" .board-column-expanded").show(),$(".board-column-header-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t).removeClass("board-column-header-collapsed"),$(".board-column-"+t).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+t).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+t)},Kanboard.BoardHorizontalScrolling=function(t){this.app=t},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var t=this;$(document).on("click",".filter-toggle-scrolling",function(e){e.preventDefault(),t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var t=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==t?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(t){this.app=t},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var t=parseInt($("#board").attr("data-check-interval"));t>0&&window.setInterval(this.check.bind(this),1e3*t)}},Kanboard.BoardPolling.prototype.check=function(){if(this.app.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(e){t.app.get("BoardDragAndDrop").refresh(e)},304:function(){t.app.hideLoadingIcon()}}})}},Kanboard.BoardTask=function(t){this.app=t},Kanboard.BoardTask.prototype.listen=function(){var t=this;$(document).on("click",".task-board-change-assignee",function(e){e.preventDefault(),e.stopPropagation(),t.app.get("Popover").open($(this).data("url"))}),$(document).on("click",".task-board",function(t){"A"!=t.target.tagName&&"IMG"!=t.target.tagName&&(window.location=$(this).data("task-url"))})},Kanboard.Column=function(t){this.app=t},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("column-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(t,e){var n=$(".columns-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Dropdown=function(t){this.app=t},Kanboard.Dropdown.prototype.listen=function(){var t=this;$(document).on("click",function(){t.close()}),$(document).on("click",".dropdown-menu",function(e){e.preventDefault(),e.stopImmediatePropagation(),t.close();var n=$(this).next("ul"),o=$(this).offset();$("body").append(jQuery("<div>",{id:"dropdown"})),n.clone().appendTo("#dropdown");var a=$("#dropdown ul");a.addClass("dropdown-submenu-open");var i=a.outerHeight(),r=a.outerWidth();o.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+o.top<i?a.css("top",o.top+$(this).height()):a.css("top",o.top-i-5),o.left+r>$(window).width()?a.css("left",o.left-r+$(this).outerWidth()):a.css("left",o.left)}),$(document).on("click",".dropdown-submenu-open li",function(t){$(t.target).is("li")&&$(this).find("a:visible")[0].click()})},Kanboard.Dropdown.prototype.close=function(){$("#dropdown").remove()},Kanboard.Gantt=function(t){this.app=t,this.data=[],this.options={container:"#gantt-chart",showWeekends:!0,allowMoves:!0,allowResizes:!0,cellWidth:21,cellHeight:31,slideWidth:1e3,vHeaderWidth:200}},Kanboard.Gantt.prototype.execute=function(){this.app.hasId("gantt-chart")&&this.show()},Kanboard.Gantt.prototype.saveRecord=function(t){this.app.showLoadingIcon(),$.ajax({cache:!1,url:$(this.options.container).data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify(t),complete:this.app.hideLoadingIcon.bind(this)})},Kanboard.Gantt.prototype.show=function(){this.data=this.prepareData($(this.options.container).data("records"));var t=Math.floor(this.options.slideWidth/this.options.cellWidth+5),e=this.getDateRange(t),n=e[0],o=e[1],a=$(this.options.container),i=jQuery("<div>",{"class":"ganttview"});i.append(this.renderVerticalHeader()),i.append(this.renderSlider(n,o)),a.append(i),jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child",a).addClass("last"),jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child",a).addClass("last"),$(this.options.container).data("readonly")?(this.options.allowResizes=!1,this.options.allowMoves=!1):(this.listenForBlockResize(n),this.listenForBlockMove(n))},Kanboard.Gantt.prototype.renderVerticalHeader=function(){for(var t=jQuery("<div>",{"class":"ganttview-vtheader"}),e=jQuery("<div>",{"class":"ganttview-vtheader-item"}),n=jQuery("<div>",{"class":"ganttview-vtheader-series"}),o=0;o<this.data.length;o++){var a=jQuery("<span>").append(jQuery("<i>",{"class":"fa fa-info-circle tooltip",title:this.getVerticalHeaderTooltip(this.data[o])})).append(" ");"task"==this.data[o].type?a.append(jQuery("<a>",{href:this.data[o].link,title:this.data[o].title}).append(this.data[o].title)):a.append(jQuery("<a>",{href:this.data[o].board_link,title:$(this.options.container).data("label-board-link")}).append('<i class="fa fa-th"></i>')).append(" ").append(jQuery("<a>",{href:this.data[o].gantt_link,title:$(this.options.container).data("label-gantt-link")}).append('<i class="fa fa-sliders"></i>')).append(" ").append(jQuery("<a>",{href:this.data[o].link}).append(this.data[o].title)),n.append(jQuery("<div>",{"class":"ganttview-vtheader-series-name"}).append(a))}return e.append(n),t.append(e),t},Kanboard.Gantt.prototype.renderSlider=function(t,e){var n=jQuery("<div>",{"class":"ganttview-slide-container"}),o=this.getDates(t,e);return n.append(this.renderHorizontalHeader(o)),n.append(this.renderGrid(o)),n.append(this.addBlockContainers()),this.addBlocks(n,t),n},Kanboard.Gantt.prototype.renderHorizontalHeader=function(t){var e=jQuery("<div>",{"class":"ganttview-hzheader"}),n=jQuery("<div>",{"class":"ganttview-hzheader-months"}),o=jQuery("<div>",{"class":"ganttview-hzheader-days"}),a=0;for(var i in t)for(var r in t[i]){var d=t[i][r].length*this.options.cellWidth;a+=d,n.append(jQuery("<div>",{"class":"ganttview-hzheader-month",css:{width:d-1+"px"}}).append($.datepicker.regional[$("body").data("js-lang")].monthNames[r]+" "+i));for(var s in t[i][r])o.append(jQuery("<div>",{"class":"ganttview-hzheader-day"}).append(t[i][r][s].getDate()))}return n.css("width",a+"px"),o.css("width",a+"px"),e.append(n).append(o),e},Kanboard.Gantt.prototype.renderGrid=function(t){var e=jQuery("<div>",{"class":"ganttview-grid"}),n=jQuery("<div>",{"class":"ganttview-grid-row"});for(var o in t)for(var a in t[o])for(var i in t[o][a]){var r=jQuery("<div>",{"class":"ganttview-grid-row-cell"});this.options.showWeekends&&this.isWeekend(t[o][a][i])&&r.addClass("ganttview-weekend"),n.append(r)}var d=jQuery("div.ganttview-grid-row-cell",n).length*this.options.cellWidth;n.css("width",d+"px"),e.css("width",d+"px");for(var s=0;s<this.data.length;s++)e.append(n.clone());return e},Kanboard.Gantt.prototype.addBlockContainers=function(){for(var t=jQuery("<div>",{"class":"ganttview-blocks"}),e=0;e<this.data.length;e++)t.append(jQuery("<div>",{"class":"ganttview-block-container"}));return t},Kanboard.Gantt.prototype.addBlocks=function(t,e){for(var n=jQuery("div.ganttview-blocks div.ganttview-block-container",t),o=0,a=0;a<this.data.length;a++){var i=this.data[a],r=this.daysBetween(i.start,i.end)+1,d=this.daysBetween(e,i.start),s=jQuery("<div>",{"class":"ganttview-block-text"}),l=jQuery("<div>",{"class":"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(i),css:{width:r*this.options.cellWidth-9+"px","margin-left":d*this.options.cellWidth+"px"}}).append(s);r>=2&&s.append(i.progress),l.data("record",i),this.setBarColor(l,i),jQuery(n[o]).append(l),o+=1}},Kanboard.Gantt.prototype.getVerticalHeaderTooltip=function(t){var e="";if("task"==t.type)e="<strong>"+t.column_title+"</strong> ("+t.progress+")<br/>"+t.title;else{var n=["project-manager","project-member"];for(var o in n){var a=n[o];if(!jQuery.isEmptyObject(t.users[a])){var i=jQuery("<ul>");for(var r in t.users[a])r&&i.append(jQuery("<li>").append(t.users[a][r]));e+="<p><strong>"+$(this.options.container).data("label-"+a)+"</strong></p>"+i[0].outerHTML}}}return e},Kanboard.Gantt.prototype.getBarTooltip=function(t){var e="";return t.not_defined?e=$(this.options.container).data("label-not-defined"):("task"==t.type&&(e="<strong>"+t.progress+"</strong><br/>"+$(this.options.container).data("label-assignee")+" "+(t.assignee?t.assignee:"")+"<br/>"),e+=$(this.options.container).data("label-start-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.start)+"<br/>",e+=$(this.options.container).data("label-end-date")+" "+$.datepicker.formatDate("yy-mm-dd",t.end)),e},Kanboard.Gantt.prototype.setBarColor=function(t,e){e.not_defined?t.addClass("ganttview-block-not-defined"):(t.css("background-color",e.color.background),t.css("border-color",e.color.border),"0%"!=e.progress&&t.append(jQuery("<div>",{css:{"z-index":0,position:"absolute",top:0,bottom:0,"background-color":e.color.border,width:e.progress,opacity:.4}})))},Kanboard.Gantt.prototype.listenForBlockResize=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).resizable({grid:this.options.cellWidth,handles:"e,w",delay:300,stop:function(){var n=jQuery(this);e.updateDataAndPosition(n,t),e.saveRecord(n.data("record"))}})},Kanboard.Gantt.prototype.listenForBlockMove=function(t){var e=this;jQuery("div.ganttview-block",this.options.container).draggable({axis:"x",delay:300,grid:[this.options.cellWidth,this.options.cellWidth],stop:function(){var n=jQuery(this);e.updateDataAndPosition(n,t),e.saveRecord(n.data("record"))}})},Kanboard.Gantt.prototype.updateDataAndPosition=function(t,e){var n=jQuery("div.ganttview-slide-container",this.options.container),o=n.scrollLeft(),a=t.offset().left-n.offset().left-1+o,i=t.data("record");i.not_defined=!1,this.setBarColor(t,i);var r=Math.round(a/this.options.cellWidth),d=this.addDays(this.cloneDate(e),r);i.start=d;var s=t.outerWidth(),l=Math.round(s/this.options.cellWidth)-1;i.end=this.addDays(this.cloneDate(d),l),"task"===i.type&&l>0&&jQuery("div.ganttview-block-text",t).text(i.progress),t.attr("title",this.getBarTooltip(i)),t.data("record",i),t.css("top","").css("left","").css("position","relative").css("margin-left",a+"px")},Kanboard.Gantt.prototype.getDates=function(t,e){var n=[];n[t.getFullYear()]=[],n[t.getFullYear()][t.getMonth()]=[t];for(var o=t;this.compareDate(o,e)==-1;){var a=this.addDays(this.cloneDate(o),1);n[a.getFullYear()]||(n[a.getFullYear()]=[]),n[a.getFullYear()][a.getMonth()]||(n[a.getFullYear()][a.getMonth()]=[]),n[a.getFullYear()][a.getMonth()].push(a),o=a}return n},Kanboard.Gantt.prototype.prepareData=function(t){for(var e=0;e<t.length;e++){var n=new Date(t[e].start[0],t[e].start[1]-1,t[e].start[2],0,0,0,0);t[e].start=n;var o=new Date(t[e].end[0],t[e].end[1]-1,t[e].end[2],0,0,0,0);t[e].end=o}return t},Kanboard.Gantt.prototype.getDateRange=function(t){for(var e=new Date,n=new Date,o=0;o<this.data.length;o++){var a=new Date;a.setTime(Date.parse(this.data[o].start));var i=new Date;i.setTime(Date.parse(this.data[o].end)),0==o&&(e=a,n=i),1==this.compareDate(e,a)&&(e=a),this.compareDate(n,i)==-1&&(n=i)}return this.daysBetween(e,n)<t&&(n=this.addDays(this.cloneDate(e),t)),e.setDate(e.getDate()-1),[e,n]},Kanboard.Gantt.prototype.daysBetween=function(t,e){if(!t||!e)return 0;for(var n=0,o=this.cloneDate(t);this.compareDate(o,e)==-1;)n+=1,this.addDays(o,1);return n},Kanboard.Gantt.prototype.isWeekend=function(t){return t.getDay()%6==0},Kanboard.Gantt.prototype.cloneDate=function(t){return new Date(t.getTime())},Kanboard.Gantt.prototype.addDays=function(t,e){return t.setDate(t.getDate()+1*e),t},Kanboard.Gantt.prototype.compareDate=function(t,e){if(isNaN(t)||isNaN(e))throw new Error(t+" - "+e);if(t instanceof Date&&e instanceof Date)return t<e?-1:t>e?1:0;throw new TypeError(t+" - "+e)},Kanboard.Search=function(t){this.app=t},Kanboard.Search.prototype.focus=function(){$(document).on("focus","#form-search",function(){var t=$("#form-search");if(t[0].setSelectionRange){var e=2*t.val().length;t[0].setSelectionRange(e,e)}})},Kanboard.Search.prototype.listen=function(){$(document).on("click",".filter-helper",function(t){t.preventDefault();var e=$(this).data("filter"),n=$(this).data("append-filter"),o=$(this).data("unique-filter"),a=$("#form-search");if(o){var i=o.substr(0,o.indexOf(":"));e=a.val().replace(new RegExp("("+i+":[#a-z0-9]+)","g"),""),e=e.replace(new RegExp("("+i+':"(.+)")',"g"),""),e=e.trim(),e+=" "+o}else n&&(e=a.val()+" "+n);a.val(e),$("form.search").submit()})},Kanboard.Subtask=function(t){this.app=t},Kanboard.Subtask.prototype.listen=function(){var t=this;this.dragAndDrop(),$(document).on("click",".subtask-toggle-status",function(e){var n=$(this);e.preventDefault(),$.ajax({cache:!1,url:n.attr("href"),success:function(e){n.hasClass("subtask-refresh-table")?$(".subtasks-table").replaceWith(e):n.replaceWith(e),t.dragAndDrop()}})}),$(document).on("click",".subtask-toggle-timer",function(e){var n=$(this);e.preventDefault(),$.ajax({cache:!1,url:n.attr("href"),success:function(e){$(".subtasks-table").replaceWith(e),t.dragAndDrop()}})})},Kanboard.Subtask.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".subtasks-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("subtask-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Subtask.prototype.savePosition=function(t,e){var n=$(".subtasks-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({subtask_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Swimlane=function(t){this.app=t},Kanboard.Swimlane.prototype.execute=function(){$(".swimlanes-table").length&&this.dragAndDrop()},Kanboard.Swimlane.prototype.listen=function(){var t=this;$(document).on("click",".board-swimlane-toggle",function(e){e.preventDefault();var n=$(this).data("swimlane-id");t.isCollapsed(n)?t.expand(n):t.collapse(n)})},Kanboard.Swimlane.prototype.onBoardRendered=function(){for(var t=this.getAllCollapsed(),e=0;e<t.length;e++)this.collapse(t[e])},Kanboard.Swimlane.prototype.getStorageKey=function(){return"hidden_swimlanes_"+$("#board").data("project-id")},Kanboard.Swimlane.prototype.expand=function(t){var e=this.getAllCollapsed(),n=e.indexOf(t);n>-1&&e.splice(n,1),localStorage.setItem(this.getStorageKey(),JSON.stringify(e)),$(".board-swimlane-columns-"+t).css("display","table-row"),$(".board-swimlane-tasks-"+t).css("display","table-row"),$(".hide-icon-swimlane-"+t).css("display","inline"),$(".show-icon-swimlane-"+t).css("display","none")},Kanboard.Swimlane.prototype.collapse=function(t){var e=this.getAllCollapsed();e.indexOf(t)<0&&(e.push(t),localStorage.setItem(this.getStorageKey(),JSON.stringify(e))),$(".board-swimlane-columns-"+t+":not(:first-child)").css("display","none"),$(".board-swimlane-tasks-"+t).css("display","none"),$(".hide-icon-swimlane-"+t).css("display","none"),$(".show-icon-swimlane-"+t).css("display","inline")},Kanboard.Swimlane.prototype.isCollapsed=function(t){return this.getAllCollapsed().indexOf(t)>-1},Kanboard.Swimlane.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]},Kanboard.Swimlane.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".swimlanes-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,n){var o=n.item;o.removeClass("draggable-item-selected"),t.savePosition(o.data("swimlane-id"),o.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Swimlane.prototype.savePosition=function(t,e){var n=$(".swimlanes-table").data("save-position-url"),o=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:n,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({swimlane_id:t,position:e}),complete:function(){o.app.hideLoadingIcon()}})},Kanboard.Task=function(t){this.app=t},Kanboard.Task.prototype.onPopoverOpened=function(){var t=this;t.renderColorPicker(),$(document).on("click",".assign-me",function(t){var e=$(this).data("current-id"),n="#"+$(this).data("target-id");t.preventDefault(),$(n+" option[value="+e+"]").length&&$(n).val(e)})},Kanboard.Task.prototype.renderColorPicker=function(){function t(t){return $('<div class="color-picker-option"><div class="color-picker-square color-'+t.id+'"></div><div class="color-picker-label">'+t.text+"</div></div>")}$(".color-picker").select2({minimumResultsForSearch:1/0,templateResult:t,templateSelection:t})},Kanboard.Tooltip=function(t){this.app=t},Kanboard.Tooltip.prototype.onBoardRendered=function(){this.execute()},Kanboard.Tooltip.prototype.execute=function(){$(".tooltip").tooltip({track:!1,show:!1,hide:!1,position:{my:"left-20 top",at:"center bottom+9",using:function(t,e){$(this).css(t);var n=e.target.left+e.target.width/2-e.element.left-20;$("<div>").addClass("tooltip-arrow").addClass(e.vertical).addClass(n<1?"align-left":"align-right").appendTo(this)}},content:function(){var t=this,e=$(this).attr("data-href");return e?($.get(e,function(e){var n=$(".ui-tooltip:visible");$(".ui-tooltip-content:visible").html(e),n.css({top:"",left:""}),n.children(".tooltip-arrow").remove();var o=$(t).tooltip("option","position");o.of=$(t),n.position(o)}),'<i class="fa fa-spinner fa-spin"></i>'):'<div class="markdown">'+$(this).attr("title")+"</div>"}}).on("mouseenter",function(){var t=this;$(this).tooltip("open"),$(".ui-tooltip").on("mouseleave",function(){$(t).tooltip("close")})}).on("mouseleave focusout",function(t){t.stopImmediatePropagation();var e=this;setTimeout(function(){$(".ui-tooltip:hover").length||$(e).tooltip("close")},100)})},Kanboard.BoardDragAndDrop=function(t){this.app=t,this.savingInProgress=!1},Kanboard.BoardDragAndDrop.prototype.execute=function(){this.app.hasId("board")&&(this.dragAndDrop(),this.executeListeners())},Kanboard.BoardDragAndDrop.prototype.dragAndDrop=function(){var t=this,e=$(".board-task-list"),n={forcePlaceholderSize:!0,tolerance:"pointer",connectWith:".sortable-column",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(e,n){var o=n.item,a=o.attr("data-task-id"),i=o.attr("data-position"),r=o.attr("data-column-id"),d=o.attr("data-swimlane-id"),s=o.parent().attr("data-column-id"),l=o.parent().attr("data-swimlane-id"),c=o.index()+1;o.removeClass("draggable-item-selected"),s==r&&l==d&&c==i||(t.changeTaskState(a),t.save(a,r,s,c,l)); +},start:function(t,e){e.item.addClass("draggable-item-selected"),e.placeholder.height(e.item.height())}};isMobile.any&&($(".task-board-sort-handle").css("display","inline"),n.handle=".task-board-sort-handle"),e.each(function(){$(this).css("min-height",$(this).parent().height())}),e.sortable(n)},Kanboard.BoardDragAndDrop.prototype.changeTaskState=function(t){var e=$("div[data-task-id="+t+"]");e.addClass("task-board-saving-state"),e.find(".task-board-saving-icon").show()},Kanboard.BoardDragAndDrop.prototype.save=function(t,e,n,o,a){var i=this;i.app.showLoadingIcon(),i.savingInProgress=!0,$.ajax({cache:!1,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:t,src_column_id:e,dst_column_id:n,swimlane_id:a,position:o}),success:function(t){i.refresh(t),i.savingInProgress=!1},error:function(){i.app.hideLoadingIcon(),i.savingInProgress=!1},statusCode:{403:function(t){window.alert(t.responseJSON.message),document.location.reload(!0)}}})},Kanboard.BoardDragAndDrop.prototype.refresh=function(t){$("#board-container").replaceWith(t),this.app.hideLoadingIcon(),this.dragAndDrop(),this.executeListeners()},Kanboard.BoardDragAndDrop.prototype.executeListeners=function(){for(var t in this.app.controllers){var e=this.app.get(t);"function"==typeof e.onBoardRendered&&e.onBoardRendered()}};var _KB=null;jQuery(document).ready(function(){_KB=new Kanboard.App,_KB.execute()});
\ No newline at end of file diff --git a/assets/js/components/submit-cancel.js b/assets/js/components/confirm-buttons.js index ccb3c1d9..81abe016 100644 --- a/assets/js/components/submit-cancel.js +++ b/assets/js/components/confirm-buttons.js @@ -1,28 +1,27 @@ -KB.component('submit-cancel', function (containerElement, options) { +KB.component('confirm-buttons', function (containerElement, options) { var isLoading = false; function onSubmit() { isLoading = true; - KB.find('#modal-submit-button').replace(buildButton()); - KB.trigger('modal.submit'); + KB.find('#modal-confirm-button').replace(buildButton()); + window.location.href = options.url; } function onCancel() { - KB.trigger('modal.cancel'); - _KB.get('Popover').close(); + KB.trigger('modal.close'); } function onStop() { isLoading = false; - KB.find('#modal-submit-button').replace(buildButton()); + KB.find('#modal-confirm-button').replace(buildButton()); } function buildButton() { var button = KB.dom('button') .click(onSubmit) - .attr('id', 'modal-submit-button') - .attr('type', 'submit') - .attr('class', 'btn btn-blue'); + .attr('id', 'modal-confirm-button') + .attr('type', 'button') + .attr('class', 'btn btn-red'); if (isLoading) { button @@ -32,6 +31,10 @@ KB.component('submit-cancel', function (containerElement, options) { ; } + if (options.tabindex) { + button.attr('tabindex', options.tabindex); + } + return button .text(options.submitLabel) .build(); diff --git a/assets/js/components/file-upload.js b/assets/js/components/file-upload.js new file mode 100644 index 00000000..762eb6e1 --- /dev/null +++ b/assets/js/components/file-upload.js @@ -0,0 +1,192 @@ +KB.component('file-upload', function (containerElement, options) { + var inputFileElement = null; + var dropzoneElement = null; + var files = []; + var currentFileIndex = null; + + function onProgress(e) { + if (e.lengthComputable) { + var progress = e.loaded / e.total; + var percentage = Math.floor(progress * 100); + + KB.find('#file-progress-' + currentFileIndex).attr('value', progress); + KB.find('#file-percentage-' + currentFileIndex).replaceText('(' + percentage + '%)'); + } + } + + function onError() { + var errorElement = KB.dom('div').addClass('file-error').text(options.labelUploadError).build(); + KB.find('#file-item-' + currentFileIndex).add(errorElement); + } + + function onComplete() { + currentFileIndex++; + + if (currentFileIndex < files.length) { + KB.http.uploadFile(options.url, files[currentFileIndex], onProgress, onComplete, onError); + } else { + KB.trigger('modal.stop'); + KB.trigger('modal.hide'); + + var alertElement = KB.dom('div') + .addClass('alert') + .addClass('alert-success') + .text(options.labelSuccess) + .build(); + + var buttonElement = KB.dom('button') + .attr('type', 'button') + .addClass('btn') + .addClass('btn-blue') + .click(onCloseWindow) + .text(options.labelCloseSuccess) + .build(); + + KB.dom(dropzoneElement).replace(KB.dom('div').add(alertElement).add(buttonElement).build()); + } + } + + function onCloseWindow() { + window.location.reload(); + } + + function onSubmit() { + currentFileIndex = 0; + uploadFiles(); + } + + function onFileChange() { + files = inputFileElement.files; + showFiles(); + } + + function onClickFileBrowser() { + files = []; + currentFileIndex = 0; + inputFileElement.click(); + } + + function onDragOver(e) { + e.stopPropagation(); + e.preventDefault(); + } + + function onDrop(e) { + e.stopPropagation(); + e.preventDefault(); + + files = e.dataTransfer.files; + showFiles(); + } + + function uploadFiles() { + if (files.length > 0) { + KB.http.uploadFile(options.url, files[currentFileIndex], onProgress, onComplete, onError); + } + } + + function showFiles() { + if (files.length > 0) { + KB.trigger('modal.enable'); + + KB.dom(dropzoneElement) + .empty() + .add(buildFileListElement()); + } else { + KB.trigger('modal.disable'); + + KB.dom(dropzoneElement) + .empty() + .add(buildInnerDropzoneElement()); + } + } + + function buildFileInputElement() { + return KB.dom('input') + .attr('id', 'file-input-element') + .attr('type', 'file') + .attr('name', 'files[]') + .attr('multiple', true) + .on('change', onFileChange) + .hide() + .build(); + } + + function buildInnerDropzoneElement() { + var dropzoneLinkElement = KB.dom('a') + .attr('href', '#') + .text(options.labelChooseFiles) + .click(onClickFileBrowser) + .build(); + + return KB.dom('div') + .attr('id', 'file-dropzone-inner') + .text(options.labelDropzone + ' ' + options.labelOr + ' ') + .add(dropzoneLinkElement) + .build(); + } + + function buildDropzoneElement() { + var dropzoneElement = KB.dom('div') + .attr('id', 'file-dropzone') + .add(buildInnerDropzoneElement()) + .build(); + + dropzoneElement.ondragover = onDragOver; + dropzoneElement.ondrop = onDrop; + dropzoneElement.ondragover = onDragOver; + + return dropzoneElement; + } + + function buildFileListItem(index) { + var isOversize = false; + var progressElement = KB.dom('progress') + .attr('id', 'file-progress-' + index) + .attr('value', 0) + .build(); + + var percentageElement = KB.dom('span') + .attr('id', 'file-percentage-' + index) + .text('(0%)') + .build(); + + var itemElement = KB.dom('li') + .attr('id', 'file-item-' + index) + .add(progressElement) + .text(' ' + files[index].name + ' ') + .add(percentageElement); + + if (files[index].size > options.maxSize) { + itemElement.add(KB.dom('div').addClass('file-error').text(options.labelOversize).build()); + isOversize = true; + } + + if (isOversize) { + KB.trigger('modal.disable'); + } + + return itemElement.build(); + } + + function buildFileListElement() { + var fileListElement = KB.dom('ul') + .attr('id', 'file-list') + .build(); + + for (var i = 0; i < files.length; i++) { + fileListElement.appendChild(buildFileListItem(i)); + } + + return fileListElement; + } + + this.render = function () { + KB.on('modal.submit', onSubmit); + + inputFileElement = buildFileInputElement(); + dropzoneElement = buildDropzoneElement(); + containerElement.appendChild(inputFileElement); + containerElement.appendChild(dropzoneElement); + }; +}); diff --git a/assets/js/components/keyboard-shortcuts.js b/assets/js/components/keyboard-shortcuts.js index 22859bd6..046e027c 100644 --- a/assets/js/components/keyboard-shortcuts.js +++ b/assets/js/components/keyboard-shortcuts.js @@ -15,19 +15,19 @@ KB.keyboardShortcuts = function () { } else if (forms.length > 1) { if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') { $(document.activeElement).parents("form").submit(); - } else if (_KB.get("Popover").isOpen()) { - $("#popover-container form").submit(); + } else if (KB.modal.isOpen()) { + KB.modal.getForm().submit(); } } } KB.onKey('?', function () { - _KB.get("Popover").open($("body").data("keyboard-shortcut-url")); + KB.modal.open(KB.find('body').data('keyboardShortcutUrl')); }); KB.onKey('Escape', function () { if (! KB.exists('#suggest-menu')) { - _KB.get("Popover").close(); + KB.trigger('modal.close'); _KB.get("Dropdown").close(); } }); @@ -49,25 +49,25 @@ KB.keyboardShortcuts = function () { }); KB.onKey('n', function () { - _KB.get("Popover").open($("#board").data("task-creation-url")); + KB.modal.open(KB.find('#board').data('taskCreationUrl'), 'large', false); }); } if (KB.exists('#task-view')) { KB.onKey('e', function () { - _KB.get("Popover").open(KB.find('#task-view').data('editUrl')); + KB.modal.open(KB.find('#task-view').data('editUrl'), 'large'); }); KB.onKey('c', function () { - _KB.get("Popover").open(KB.find('#task-view').data('commentUrl')); + KB.modal.open(KB.find('#task-view').data('commentUrl')); }); KB.onKey('s', function () { - _KB.get("Popover").open(KB.find('#task-view').data('subtaskUrl')); + KB.modal.open(KB.find('#task-view').data('subtaskUrl')); }); KB.onKey('l', function () { - _KB.get("Popover").open(KB.find('#task-view').data('internalLinkUrl')); + KB.modal.open(KB.find('#task-view').data('internalLinkUrl')); }); } diff --git a/assets/js/components/modal.js b/assets/js/components/modal.js new file mode 100644 index 00000000..f0867289 --- /dev/null +++ b/assets/js/components/modal.js @@ -0,0 +1,29 @@ +(function () { + function getLink(e) { + if (e.target.tagName === 'I') { + return e.target.parentNode.getAttribute('href'); + } + + return e.target.getAttribute('href') + } + + KB.onClick('.js-modal-large', function (e) { + KB.modal.open(getLink(e), 'large', false); + }); + + KB.onClick('.js-modal-medium', function (e) { + KB.modal.open(getLink(e), 'medium', false); + }); + + KB.onClick('.js-modal-small', function (e) { + KB.modal.open(getLink(e), 'small', false); + }); + + KB.onClick('.js-modal-confirm', function (e) { + KB.modal.open(getLink(e), 'small'); + }); + + KB.onClick('.js-modal-close', function () { + KB.modal.close(); + }); +}()); diff --git a/assets/js/components/screenshot.js b/assets/js/components/screenshot.js new file mode 100644 index 00000000..a110f9b1 --- /dev/null +++ b/assets/js/components/screenshot.js @@ -0,0 +1,120 @@ +KB.component('screenshot', function (containerElement) { + var pasteCatcher = null; + var inputElement = null; + + function onPaste(e) { + // Firefox doesn't have the property e.clipboardData.items (only Chrome) + if (e.clipboardData && e.clipboardData.items) { + var items = e.clipboardData.items; + + if (items) { + for (var i = 0; i < items.length; i++) { + // Find an image in pasted elements + if (items[i].type.indexOf("image") !== -1) { + + var blob = items[i].getAsFile(); + + // Get the image as base64 data + var reader = new FileReader(); + reader.onload = function (event) { + createImage(event.target.result); + }; + + reader.readAsDataURL(blob); + } + } + } + } else { + + // Handle Firefox + setTimeout(checkInput, 100); + } + } + + function initialize() { + destroy(); + + if (! window.Clipboard) { + // Insert the content editable at the top to avoid scrolling down in the board view + pasteCatcher = document.createElement('div'); + pasteCatcher.id = 'screenshot-pastezone'; + pasteCatcher.contentEditable = true; + pasteCatcher.style.opacity = 0; + pasteCatcher.style.position = 'fixed'; + pasteCatcher.style.top = 0; + pasteCatcher.style.right = 0; + pasteCatcher.style.width = 0; + document.body.insertBefore(pasteCatcher, document.body.firstChild); + + pasteCatcher.focus(); + + // Set the focus when clicked anywhere in the document + document.addEventListener('click', setFocus); + + // Set the focus when clicked in screenshot dropzone + document.getElementById('screenshot-zone').addEventListener('click', setFocus); + } + + window.addEventListener('paste', onPaste, false); + } + + function destroy() { + if (pasteCatcher !== null) { + document.body.removeChild(pasteCatcher); + } else if (document.getElementById('screenshot-pastezone')) { + document.body.removeChild(document.getElementById('screenshot-pastezone')); + } + + document.removeEventListener('click', setFocus); + pasteCatcher = null; + } + + function setFocus() { + if (pasteCatcher !== null) { + pasteCatcher.focus(); + } + } + + function checkInput() { + var child = pasteCatcher.childNodes[0]; + + if (child) { + // If the user pastes an image, the src attribute + // will represent the image as a base64 encoded string. + if (child.tagName === 'IMG') { + createImage(child.src); + } + } + + pasteCatcher.innerHTML = ''; + } + + function createImage(blob) { + var pastedImage = new Image(); + pastedImage.src = blob; + + // Send the image content to the form variable + pastedImage.onload = function() { + var sourceSplit = blob.split('base64,'); + inputElement.value = sourceSplit[1]; + }; + + var zone = document.getElementById('screenshot-zone'); + zone.innerHTML = ''; + zone.className = 'screenshot-pasted'; + zone.appendChild(pastedImage); + + destroy(); + initialize(); + } + + this.render = function () { + inputElement = KB.dom('input') + .attr('type', 'hidden') + .attr('name', 'screenshot') + .build(); + + containerElement.appendChild(inputElement); + initialize(); + }; +}); diff --git a/assets/js/components/select-dropdown-autocomplete.js b/assets/js/components/select-dropdown-autocomplete.js index 9a8df180..adf48470 100644 --- a/assets/js/components/select-dropdown-autocomplete.js +++ b/assets/js/components/select-dropdown-autocomplete.js @@ -1,5 +1,24 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) { - var componentElement, inputElement, inputHiddenElement; + var componentElement, inputElement, inputHiddenElement, chevronIconElement, loadingIconElement; + + function onLoadingStart() { + KB.dom(loadingIconElement).show(); + KB.dom(chevronIconElement).hide(); + } + + function onLoadingStop() { + KB.dom(loadingIconElement).hide(); + KB.dom(chevronIconElement).show(); + } + + function onScroll() { + var menuElement = KB.find('#select-dropdown-menu'); + + if (menuElement) { + var componentPosition = componentElement.getBoundingClientRect(); + menuElement.style('top', (document.body.scrollTop + componentPosition.bottom) + 'px'); + } + } function onKeyDown(e) { switch (KB.utils.getKey(e)) { @@ -66,8 +85,10 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) destroyDropdownMenu(); if (options.redirect) { - var regex = new RegExp(options.redirect.regex, 'g'); - window.location = options.redirect.url.replace(regex, value); + window.location = options.redirect.url.replace(new RegExp(options.redirect.regex, 'g'), value); + } else if (options.replace) { + onLoadingStart(); + KB.modal.replace(options.replace.url.replace(new RegExp(options.replace.regex, 'g'), value)); } } @@ -158,7 +179,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) return KB.dom('ul') .attr('id', 'select-dropdown-menu') - .style('top', componentPosition.bottom + 'px') + .style('top', (document.body.scrollTop + componentPosition.bottom) + 'px') .style('left', componentPosition.left + 'px') .style('width', componentPosition.width + 'px') .style('maxHeight', (window.innerHeight - componentPosition.bottom - 20) + 'px') @@ -203,11 +224,20 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) } this.render = function () { - var dropdownIconElement = KB.dom('i') + KB.on('select.dropdown.loading.start', onLoadingStart); + KB.on('select.dropdown.loading.stop', onLoadingStop); + + chevronIconElement = KB.dom('i') .attr('class', 'fa fa-chevron-down select-dropdown-chevron') .click(toggleDropdownMenu) .build(); + loadingIconElement = KB.dom('span') + .hide() + .addClass('select-loading-icon') + .add(KB.dom('i').attr('class', 'fa fa-spinner fa-pulse').build()) + .build(); + inputHiddenElement = KB.dom('input') .attr('type', 'hidden') .attr('name', options.name) @@ -227,7 +257,8 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) .addClass('select-dropdown-input-container') .add(inputHiddenElement) .add(inputElement) - .add(dropdownIconElement) + .add(chevronIconElement) + .add(loadingIconElement) .build(); containerElement.appendChild(componentElement); @@ -237,5 +268,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options) KB.on(eventName, function() { inputElement.focus(); }); }); } + + window.addEventListener('scroll', onScroll, false); }; }); diff --git a/assets/js/components/submit-buttons.js b/assets/js/components/submit-buttons.js new file mode 100644 index 00000000..b6ad3769 --- /dev/null +++ b/assets/js/components/submit-buttons.js @@ -0,0 +1,94 @@ +KB.component('submit-buttons', function (containerElement, options) { + var isLoading = false; + var isDisabled = options.disabled || false; + var submitLabel = options.submitLabel; + var formActionElement = null; + + function onSubmit() { + isLoading = true; + KB.find('#modal-submit-button').replace(buildButton()); + KB.trigger('modal.submit'); + } + + function onCancel() { + KB.trigger('modal.close'); + } + + function onStop() { + isLoading = false; + KB.find('#modal-submit-button').replace(buildButton()); + } + + function onDisable() { + isLoading = false; + isDisabled = true; + KB.find('#modal-submit-button').replace(buildButton()); + } + + function onEnable() { + isLoading = false; + isDisabled = false; + KB.find('#modal-submit-button').replace(buildButton()); + } + + function onHide() { + KB.dom(formActionElement).hide(); + } + + function onUpdateSubmitLabel(eventData) { + submitLabel = eventData.submitLabel; + KB.find('#modal-submit-button').replace(buildButton()); + } + + function buildButton() { + var button = KB.dom('button') + .attr('id', 'modal-submit-button') + .attr('type', 'submit') + .attr('class', 'btn btn-' + (options.color || 'blue')); + + if (KB.modal.isOpen()) { + button.click(onSubmit); + } + + if (options.tabindex) { + button.attr('tabindex', options.tabindex); + } + + if (isLoading) { + button + .disable() + .add(KB.dom('i').attr('class', 'fa fa-spinner fa-pulse').build()) + .text(' ') + ; + } + + if (isDisabled) { + button.disable(); + } + + return button + .text(submitLabel) + .build(); + } + + this.render = function () { + KB.on('modal.stop', onStop); + KB.on('modal.disable', onDisable); + KB.on('modal.enable', onEnable); + KB.on('modal.hide', onHide); + KB.on('modal.submit.label', onUpdateSubmitLabel); + + var formActionElementBuilder = KB.dom('div') + .attr('class', 'form-actions') + .add(buildButton()); + + if (KB.modal.isOpen()) { + formActionElementBuilder + .text(' ' + options.orLabel + ' ') + .add(KB.dom('a').attr('href', '#').click(onCancel).text(options.cancelLabel).build()) + } + + formActionElement = formActionElementBuilder.build(); + containerElement.appendChild(formActionElement); + }; +}); diff --git a/assets/js/components/suggest-menu.js b/assets/js/components/suggest-menu.js index 238a7d77..7f3e6f62 100644 --- a/assets/js/components/suggest-menu.js +++ b/assets/js/components/suggest-menu.js @@ -62,7 +62,7 @@ KB.component('suggest-menu', function(containerElement, options) { } function getParentElement() { - var selectors = ['.popover-form', '#popover-content', 'body']; + var selectors = ['#modal-content form', '#modal-content', 'body']; for (var i = 0; i < selectors.length; i++) { var element = document.querySelector(selectors[i]); diff --git a/assets/js/components/task-move-position.js b/assets/js/components/task-move-position.js index e54c1324..a1cd969e 100644 --- a/assets/js/components/task-move-position.js +++ b/assets/js/components/task-move-position.js @@ -74,8 +74,6 @@ KB.component('task-move-position', function (containerElement, options) { "column_id": getColumnId(), "swimlane_id": getSwimlaneId(), "position": position - }).success(function () { - window.location.reload(true); }).error(function (response) { if (response) { onError(response.message); @@ -150,7 +148,6 @@ KB.component('task-move-position', function (containerElement, options) { KB.on('modal.submit', onSubmit); var form = KB.dom('div') - .on('submit', onSubmit) .add(KB.dom('div').attr('id', 'message-container').build()) .add(KB.html.label(options.swimlaneLabel, 'form-swimlanes')) .add(buildSwimlaneSelect()) diff --git a/assets/js/core/base.js b/assets/js/core/base.js index a4fd00ac..033be0a7 100644 --- a/assets/js/core/base.js +++ b/assets/js/core/base.js @@ -22,9 +22,7 @@ KB.on = function (eventType, callback) { KB.trigger = function (eventType, eventData) { if (this.listeners.internals.hasOwnProperty(eventType)) { for (var i = 0; i < this.listeners.internals[eventType].length; i++) { - if (! this.listeners.internals[eventType][i](eventData)) { - break; - } + this.listeners.internals[eventType][i](eventData); } } }; diff --git a/assets/js/core/dom.js b/assets/js/core/dom.js index 84bdade2..250a1f78 100644 --- a/assets/js/core/dom.js +++ b/assets/js/core/dom.js @@ -88,6 +88,11 @@ KB.dom = function (tag) { return this; }; + this.replaceText = function (text) { + element.textContent = text; + return this; + }; + this.addClass = function (className) { element.classList.add(className); return this; @@ -122,6 +127,13 @@ KB.dom = function (tag) { return this; }; + this.empty = function () { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + return this; + }; + this.parent = function (selector) { for (; element && element !== document; element = element.parentNode) { if (element.matches(selector)) { diff --git a/assets/js/core/http.js b/assets/js/core/http.js index b965ad23..21200a0f 100644 --- a/assets/js/core/http.js +++ b/assets/js/core/http.js @@ -3,11 +3,21 @@ KB.http.request = function (method, url, headers, body) { var errorCallback = function() {}; function parseResponse(request) { - try { - return JSON.parse(request.responseText); - } catch (e) { - return request.responseText; + var redirect = request.getResponseHeader('X-Ajax-Redirect'); + + if (redirect === 'self') { + window.location.reload(); + } else if (redirect && redirect.indexOf('#') > -1) { + window.location = redirect.split('#')[0]; + } else if (redirect) { + window.location = redirect; + } else if (request.getResponseHeader('Content-Type') === 'application/json') { + try { + return JSON.parse(request.responseText); + } catch (e) {} } + + return request.responseText; } this.execute = function () { @@ -64,3 +74,20 @@ KB.http.postJson = function (url, body) { return (new KB.http.request('POST', url, headers, JSON.stringify(body))).execute(); }; + +KB.http.postForm = function (url, formElement) { + var formData = new FormData(formElement); + return (new KB.http.request('POST', url, {}, formData)).execute(); +}; + +KB.http.uploadFile = function (url, file, onProgress, onComplete, onError) { + var fd = new FormData(); + fd.append('files[]', file); + + var xhr = new XMLHttpRequest(); + xhr.upload.addEventListener('progress', onProgress); + xhr.upload.addEventListener('load', onComplete); + xhr.upload.addEventListener('error', onError); + xhr.open('POST', url, true); + xhr.send(fd); +}; diff --git a/assets/js/core/modal.js b/assets/js/core/modal.js new file mode 100644 index 00000000..b41dfbac --- /dev/null +++ b/assets/js/core/modal.js @@ -0,0 +1,172 @@ +(function () { + var isOpen = false; + + function onOverlayClick(e) { + if (e.target.matches('#modal-overlay')) { + e.stopPropagation(); + e.preventDefault(); + destroy(); + } + } + + function onCloseButtonClick() { + destroy(); + } + + function onFormSubmit() { + KB.trigger('modal.loading'); + submitForm(); + } + + function getForm() { + return document.querySelector('#modal-content form'); + } + + function submitForm() { + var form = getForm(); + + if (form) { + var url = form.getAttribute('action'); + + if (url) { + KB.http.postForm(url, form).success(function (response) { + KB.trigger('modal.stop'); + + if (response) { + replace(response); + } else { + destroy(); + } + }); + } + } + } + + function afterRendering() { + var formElement = KB.find('#modal-content form'); + if (formElement) { + formElement.on('submit', onFormSubmit, false); + } + + var autoFocusElement = document.querySelector('#modal-content input[autofocus]'); + if (autoFocusElement) { + autoFocusElement.focus(); + } + + KB.render(); + _KB.datePicker(); + _KB.autoComplete(); + _KB.tagAutoComplete(); + _KB.get('Task').onPopoverOpened(); + } + + function replace(html) { + var contentElement = KB.find('#modal-content'); + + if (contentElement) { + contentElement.replace(KB.dom('div') + .attr('id', 'modal-content') + .html(html) + .build() + ); + + afterRendering(); + } + } + + function create(html, width, overlayClickDestroy) { + var closeButtonElement = KB.dom('a') + .attr('href', '#') + .attr('id', 'modal-close-button') + .html('<i class="fa fa-times"></i>') + .click(onCloseButtonClick) + .build(); + + var headerElement = KB.dom('div') + .attr('id', 'modal-header') + .add(closeButtonElement) + .build(); + + var contentElement = KB.dom('div') + .attr('id', 'modal-content') + .html(html) + .build(); + + var boxElement = KB.dom('div') + .attr('id', 'modal-box') + .style('width', width) + .add(headerElement) + .add(contentElement) + .build(); + + var overlayElement = KB.dom('div') + .attr('id', 'modal-overlay') + .add(boxElement) + .build(); + + if (overlayClickDestroy) { + overlayElement.addEventListener('click', onOverlayClick, false); + } + + document.body.appendChild(overlayElement); + afterRendering(); + } + + function destroy() { + var overlayElement = KB.find('#modal-overlay'); + + if (overlayElement) { + overlayElement.remove(); + } + } + + function getWidth(size) { + var viewport = KB.utils.getViewportSize(); + + switch (size) { + case 'large': + return viewport.width < 1300 ? '95%' : '1300px'; + case 'medium': + return viewport.width < 1024 ? '70%' : '1024px'; + } + + return viewport.width < 800 ? '75%' : '800px'; + } + + KB.on('modal.close', function () { + destroy(); + }); + + KB.on('modal.submit', function () { + submitForm(); + }); + + KB.modal = { + open: function (url, size, overlayClickDestroy) { + _KB.get('Dropdown').close(); + destroy(); + + if (typeof overlayClickDestroy === 'undefined') { + overlayClickDestroy = true; + } + + KB.http.get(url).success(function (response) { + isOpen = true; + create(response, getWidth(size), overlayClickDestroy); + }); + }, + close: function () { + isOpen = false; + destroy(); + }, + isOpen: function () { + return isOpen; + }, + replace: function (url) { + KB.http.get(url).success(function (response) { + replace(response); + }); + }, + getForm: getForm + }; +}()); diff --git a/assets/js/core/utils.js b/assets/js/core/utils.js index 4d0f8847..a3683122 100644 --- a/assets/js/core/utils.js +++ b/assets/js/core/utils.js @@ -87,3 +87,10 @@ KB.utils.getKey = function (e) { return e.key; }; + +KB.utils.getViewportSize = function () { + return { + width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + }; +}; diff --git a/assets/js/src/BoardTask.js b/assets/js/src/BoardTask.js index cdf32532..9e51002b 100644 --- a/assets/js/src/BoardTask.js +++ b/assets/js/src/BoardTask.js @@ -2,6 +2,7 @@ Kanboard.BoardTask = function(app) { this.app = app; }; +// TODO: rewrite this code Kanboard.BoardTask.prototype.listen = function() { var self = this; diff --git a/assets/js/src/Dropdown.js b/assets/js/src/Dropdown.js index d97b925e..ec033b3e 100644 --- a/assets/js/src/Dropdown.js +++ b/assets/js/src/Dropdown.js @@ -2,6 +2,7 @@ Kanboard.Dropdown = function(app) { this.app = app; }; +// TODO: rewrite this code Kanboard.Dropdown.prototype.listen = function() { var self = this; @@ -9,10 +10,6 @@ Kanboard.Dropdown.prototype.listen = function() { self.close(); }); - $(document).on('click', '#popover-content', function() { - self.close(); - }); - $(document).on('click', '.dropdown-menu', function(e) { e.preventDefault(); e.stopImmediatePropagation(); @@ -56,7 +53,3 @@ Kanboard.Dropdown.prototype.listen = function() { Kanboard.Dropdown.prototype.close = function() { $("#dropdown").remove(); }; - -Kanboard.Dropdown.prototype.onPopoverOpened = function() { - this.close(); -}; diff --git a/assets/js/src/FileUpload.js b/assets/js/src/FileUpload.js deleted file mode 100644 index f53b9bf8..00000000 --- a/assets/js/src/FileUpload.js +++ /dev/null @@ -1,125 +0,0 @@ -Kanboard.FileUpload = function(app) { - this.app = app; - this.files = []; - this.currentFile = 0; -}; - -Kanboard.FileUpload.prototype.onPopoverOpened = function() { - var dropzone = document.getElementById("file-dropzone"); - var self = this; - - if (dropzone) { - dropzone.ondragover = dropzone.ondragenter = function(e) { - e.stopPropagation(); - e.preventDefault(); - }; - - dropzone.ondrop = function(e) { - e.stopPropagation(); - e.preventDefault(); - self.files = e.dataTransfer.files; - self.show(); - $("#file-error-max-size").hide(); - }; - - $(document).on("click", "#file-browser", function(e) { - e.preventDefault(); - $("#file-form-element").get(0).click(); - }); - - $(document).on("click", "#file-upload-button", function(e) { - e.preventDefault(); - self.currentFile = 0; - self.checkFiles(); - }); - - $("#file-form-element").change(function() { - self.files = document.getElementById("file-form-element").files; - self.show(); - $("#file-error-max-size").hide(); - }); - } -}; - -Kanboard.FileUpload.prototype.show = function() { - $("#file-list").remove(); - - if (this.files.length > 0) { - $("#file-upload-button").prop("disabled", false); - $("#file-dropzone-inner").hide(); - - var ul = jQuery("<ul>", {"id": "file-list"}); - - for (var i = 0; i < this.files.length; i++) { - var percentage = jQuery("<span>", {"id": "file-percentage-" + i}).append("(0%)"); - var progress = jQuery("<progress>", {"id": "file-progress-" + i, "value": 0}); - var li = jQuery("<li>", {"id": "file-label-" + i}) - .append(progress) - .append(" ") - .append(this.files[i].name) - .append(" ") - .append(percentage); - - ul.append(li); - } - - $("#file-dropzone").append(ul); - } else { - $("#file-dropzone-inner").show(); - } -}; - -Kanboard.FileUpload.prototype.checkFiles = function() { - var max = parseInt($("#file-dropzone").data("max-size")); - - for (var i = 0; i < this.files.length; i++) { - if (this.files[i].size > max) { - $("#file-error-max-size").show(); - $("#file-label-" + i).addClass("file-error"); - $("#file-upload-button").prop("disabled", true); - return; - } - } - - this.uploadFiles(); -}; - -Kanboard.FileUpload.prototype.uploadFiles = function() { - if (this.files.length > 0) { - this.uploadFile(this.files[this.currentFile]); - } -}; - -Kanboard.FileUpload.prototype.uploadFile = function(file) { - var dropzone = document.getElementById("file-dropzone"); - var url = dropzone.dataset.url; - var xhr = new XMLHttpRequest(); - var fd = new FormData(); - - xhr.upload.addEventListener("progress", this.updateProgress.bind(this)); - xhr.upload.addEventListener("load", this.transferComplete.bind(this)); - - xhr.open("POST", url, true); - fd.append('files[]', file); - xhr.send(fd); -}; - -Kanboard.FileUpload.prototype.updateProgress = function(e) { - if (e.lengthComputable) { - $("#file-progress-" + this.currentFile).val(e.loaded / e.total); - $("#file-percentage-" + this.currentFile).text('(' + Math.floor((e.loaded / e.total) * 100) + '%)'); - } -}; - -Kanboard.FileUpload.prototype.transferComplete = function() { - this.currentFile++; - - if (this.currentFile < this.files.length) { - this.uploadFile(this.files[this.currentFile]); - } else { - var uploadButton = $("#file-upload-button"); - uploadButton.prop("disabled", true); - uploadButton.parent().hide(); - $("#file-done").show(); - } -}; diff --git a/assets/js/src/Popover.js b/assets/js/src/Popover.js deleted file mode 100644 index ab71505a..00000000 --- a/assets/js/src/Popover.js +++ /dev/null @@ -1,161 +0,0 @@ -Kanboard.Popover = function(app) { - this.app = app; -}; - -Kanboard.Popover.prototype.listen = function() { - var self = this; - - $(document).on("click", ".popover", function(e) { - self.onClick(e); - }); - - $(document).on("click", ".close-popover", function(e) { - self.close(e); - }); - - $(document).on("click", "#popover-close-button", function(e) { - self.close(e); - }); - - $(document).on("click", "#popover-content", function(e) { - e.stopPropagation(); - }); -}; - -Kanboard.Popover.prototype.onClick = function(e) { - e.preventDefault(); - e.stopPropagation(); - - var target = e.currentTarget || e.target; - var link = target.getAttribute("href"); - - if (! link) { - link = target.getAttribute("data-href"); - } - - if (link) { - this.open(link); - } -}; - -Kanboard.Popover.prototype.isOpen = function() { - return $('#popover-container').size() > 0; -}; - -Kanboard.Popover.prototype.open = function(link) { - var self = this; - - if (!self.isOpen()) { - $.get(link, function(content) { - $("body").prepend( - '<div id="popover-container">' + - '<div id="popover-content">' + - '<div id="popover-content-header"><a href="#" id="popover-close-button"><i class="fa fa-times"></i></a></div>' + - content + - '</div>' + - '</div>' - ); - - self.executeOnOpenedListeners(); - }); - } -}; - -Kanboard.Popover.prototype.close = function(e) { - if (this.isOpen()) { - if (e) { - e.preventDefault(); - } - - $("#popover-container").remove(); - this.executeOnClosedListeners(); - } -}; - -Kanboard.Popover.prototype.ajaxReload = function(data, request, self) { - var redirect = request.getResponseHeader("X-Ajax-Redirect"); - - if (redirect === 'self') { - window.location.reload(); - } else if (redirect && redirect.indexOf('#') > -1) { - window.location = redirect.split('#')[0]; - } else if (redirect) { - window.location = redirect; - } else { - $("#popover-content").html(data); - $("#popover-content input[autofocus]").focus(); - self.executeOnOpenedListeners(); - } -}; - -Kanboard.Popover.prototype.executeOnOpenedListeners = function() { - for (var className in this.app.controllers) { - var controller = this.app.get(className); - - if (typeof controller.onPopoverOpened === "function") { - controller.onPopoverOpened(); - } - } - - this.afterOpen(); -}; - -Kanboard.Popover.prototype.executeOnClosedListeners = function() { - for (var className in this.app.controllers) { - var controller = this.app.get(className); - - if (typeof controller.onPopoverClosed === "function") { - controller.onPopoverClosed(); - } - } -}; - -Kanboard.Popover.prototype.afterOpen = function() { - var self = this; - var popoverForm = $("#popover-content .popover-form"); - - // Submit forms with Ajax request - if (popoverForm) { - popoverForm.on("submit", function(e) { - e.preventDefault(); - - $.ajax({ - type: "POST", - url: popoverForm.attr("action"), - data: popoverForm.serialize(), - success: function(data, textStatus, request) { - self.ajaxReload(data, request, self); - }, - beforeSend: function() { - var button = $('.popover-form button[type="submit"]'); - button.html('<i class="fa fa-spinner fa-pulse"></i> ' + button.html()); - button.attr("disabled", true); - } - }); - }); - } - - // Submit link with Ajax request - $(document).on("click", ".popover-link", function(e) { - e.preventDefault(); - - $.ajax({ - type: "GET", - url: $(this).attr("href"), - success: function(data, textStatus, request) { - self.ajaxReload(data, request, self); - } - }); - }); - - // Autofocus fields (html5 autofocus works only with page onload) - $("#popover-content input[autofocus]").each(function() { - $(this).focus(); - }); - - this.app.datePicker(); - this.app.autoComplete(); - this.app.tagAutoComplete(); - - KB.render(); -}; diff --git a/assets/js/src/Screenshot.js b/assets/js/src/Screenshot.js deleted file mode 100644 index 4f69e9c3..00000000 --- a/assets/js/src/Screenshot.js +++ /dev/null @@ -1,134 +0,0 @@ -Kanboard.Screenshot = function(app) { - this.app = app; - this.pasteCatcher = null; -}; - -Kanboard.Screenshot.prototype.onPopoverOpened = function() { - if (this.app.hasId("screenshot-zone")) { - this.initialize(); - } -}; - -// Setup event listener and workarounds -Kanboard.Screenshot.prototype.initialize = function() { - this.destroy(); - - if (! window.Clipboard) { - - // Create a contenteditable element - this.pasteCatcher = document.createElement("div"); - this.pasteCatcher.id = "screenshot-pastezone"; - this.pasteCatcher.contentEditable = "true"; - - // Insert the content editable at the top to avoid scrolling down in the board view - this.pasteCatcher.style.opacity = 0; - this.pasteCatcher.style.position = "fixed"; - this.pasteCatcher.style.top = 0; - this.pasteCatcher.style.right = 0; - this.pasteCatcher.style.width = 0; - - document.body.insertBefore(this.pasteCatcher, document.body.firstChild); - - // Set focus on the contenteditable element - this.pasteCatcher.focus(); - - // Set the focus when clicked anywhere in the document - document.addEventListener("click", this.setFocus.bind(this)); - - // Set the focus when clicked in screenshot dropzone (popover) - document.getElementById("screenshot-zone").addEventListener("click", this.setFocus.bind(this)); - } - - window.addEventListener("paste", this.pasteHandler.bind(this)); -}; - -// Destroy contentEditable element -Kanboard.Screenshot.prototype.destroy = function() { - if (this.pasteCatcher !== null) { - document.body.removeChild(this.pasteCatcher); - } - else if (document.getElementById("screenshot-pastezone")) { - document.body.removeChild(document.getElementById("screenshot-pastezone")); - } - - document.removeEventListener("click", this.setFocus.bind(this)); - this.pasteCatcher = null; -}; - -// Set focus on contentEditable element -Kanboard.Screenshot.prototype.setFocus = function() { - if (this.pasteCatcher !== null) { - this.pasteCatcher.focus(); - } -}; - -// Paste event callback -Kanboard.Screenshot.prototype.pasteHandler = function(e) { - // Firefox doesn't have the property e.clipboardData.items (only Chrome) - if (e.clipboardData && e.clipboardData.items) { - - var items = e.clipboardData.items; - - if (items) { - - for (var i = 0; i < items.length; i++) { - - // Find an image in pasted elements - if (items[i].type.indexOf("image") !== -1) { - - var blob = items[i].getAsFile(); - - // Get the image as base64 data - var reader = new FileReader(); - var self = this; - reader.onload = function(event) { - self.createImage(event.target.result); - }; - - reader.readAsDataURL(blob); - } - } - } - } - else { - - // Handle Firefox - setTimeout(this.checkInput.bind(this), 100); - } -}; - -// Parse the input in the paste catcher element -Kanboard.Screenshot.prototype.checkInput = function() { - var child = this.pasteCatcher.childNodes[0]; - - if (child) { - // If the user pastes an image, the src attribute - // will represent the image as a base64 encoded string. - if (child.tagName === "IMG") { - this.createImage(child.src); - } - } - - this.pasteCatcher.innerHTML = ""; -}; - -// Creates a new image from a given source -Kanboard.Screenshot.prototype.createImage = function(blob) { - var pastedImage = new Image(); - pastedImage.src = blob; - - // Send the image content to the form variable - pastedImage.onload = function() { - var sourceSplit = blob.split("base64,"); - var sourceString = sourceSplit[1]; - $("input[name=screenshot]").val(sourceString); - }; - - var zone = document.getElementById("screenshot-zone"); - zone.innerHTML = ""; - zone.className = "screenshot-pasted"; - zone.appendChild(pastedImage); - - this.destroy(); - this.initialize(); -}; diff --git a/assets/js/src/Search.js b/assets/js/src/Search.js index de78c896..b61710a4 100644 --- a/assets/js/src/Search.js +++ b/assets/js/src/Search.js @@ -15,6 +15,7 @@ Kanboard.Search.prototype.focus = function() { }); }; +// TODO: rewrite this code Kanboard.Search.prototype.listen = function() { $(document).on("click", ".filter-helper", function (e) { e.preventDefault(); diff --git a/assets/js/src/Task.js b/assets/js/src/Task.js index be9c2bfa..19cf39e8 100644 --- a/assets/js/src/Task.js +++ b/assets/js/src/Task.js @@ -2,9 +2,9 @@ Kanboard.Task = function(app) { this.app = app; }; +// TODO: rewrite this code Kanboard.Task.prototype.onPopoverOpened = function() { var self = this; - var reloadingProjectId = 0; self.renderColorPicker(); @@ -19,29 +19,6 @@ Kanboard.Task.prototype.onPopoverOpened = function() { $(dropdownId).val(currentId); } }); - - // Reload page when a destination project is changed - $(document).on("change", "select.task-reload-project-destination", function() { - if (reloadingProjectId > 0) { - $(this).val(reloadingProjectId); - } - else { - reloadingProjectId = $(this).val(); - var url = $(this).data("redirect").replace(/PROJECT_ID/g, reloadingProjectId); - - $(".loading-icon").show(); - - $.ajax({ - type: "GET", - url: url, - success: function(data, textStatus, request) { - reloadingProjectId = 0; - $(".loading-icon").hide(); - self.app.get("Popover").ajaxReload(data, request, self.app.get("Popover")); - } - }); - } - }); }; Kanboard.Task.prototype.renderColorPicker = function() { diff --git a/assets/sass/_board.sass b/assets/sass/_board.sass index c94879bc..d7d10d2e 100644 --- a/assets/sass/_board.sass +++ b/assets/sass/_board.sass @@ -65,11 +65,10 @@ td.board-column-task-collapsed .board-add-icon float: left padding: 0 5px - a + i text-decoration: none color: link-color('primary') - font-size: size('xlarge') - line-height: 70% + font-size: size('large') &:focus, &:hover text-decoration: none color: red diff --git a/assets/sass/_file_upload.sass b/assets/sass/_file_upload.sass index b258620e..bdd31a62 100644 --- a/assets/sass/_file_upload.sass +++ b/assets/sass/_file_upload.sass @@ -26,6 +26,6 @@ padding-bottom: 8px border-bottom: 1px dotted #ddd width: 95% - &.file-error + .file-error font-weight: bold color: color('error') diff --git a/assets/sass/_form.sass b/assets/sass/_form.sass index ace988f5..855f7714 100644 --- a/assets/sass/_form.sass +++ b/assets/sass/_form.sass @@ -1,13 +1,19 @@ @import variables @import mixins -form - margin-bottom: 20px +fieldset + border: 1px solid #ccc + margin-top: 20px + +legend + font-weight: 500 + font-size: size('medium') label cursor: pointer display: block margin-top: 10px + font-weight: 400 input &[type="number"], &[type="date"], &[type="email"], &[type="password"], &[type="text"]:not(.input-addon-field) @@ -19,7 +25,6 @@ input height: 25px padding-bottom: 0 font-family: sans-serif - margin-top: 10px +appearance @include placeholder color: color('lighter') diff --git a/assets/sass/_links.sass b/assets/sass/_links.sass index d4544391..3c34b964 100644 --- a/assets/sass/_links.sass +++ b/assets/sass/_links.sass @@ -10,3 +10,6 @@ a &:hover color: link-color('hover') text-decoration: none + .fa + text-decoration: none + color: color('primary')
\ No newline at end of file diff --git a/assets/sass/_markdown_editor.sass b/assets/sass/_markdown_editor.sass index b9279be9..ac25101e 100644 --- a/assets/sass/_markdown_editor.sass +++ b/assets/sass/_markdown_editor.sass @@ -1,6 +1,7 @@ @import variables .text-editor + margin-top: 10px a font-size: size('normal') color: color('light') @@ -10,7 +11,12 @@ color: link-color('primary') .text-editor-preview-area border: 1px solid color('lighter') - width: 400px - height: 200px + width: 700px + max-width: 99% + height: 250px overflow: auto padding: 2px + textarea + width: 700px + max-width: 98% + height: 250px
\ No newline at end of file diff --git a/assets/sass/_popover.sass b/assets/sass/_modal.sass index b45f8401..7ade3be5 100644 --- a/assets/sass/_popover.sass +++ b/assets/sass/_modal.sass @@ -1,7 +1,7 @@ @import variables @import mixins -#popover-container +#modal-overlay position: fixed top: 0 left: 0 @@ -11,25 +11,24 @@ overflow: auto z-index: 100 -#popover-content +#modal-box position: fixed - width: 950px - max-width: 95% max-height: calc(100% - 50px) - top: 5% + top: 2% left: 50% transform: translateX(-50%) - padding: 0 15px 15px background: #fff overflow: auto + border-radius: 5px -#popover-content-header +#modal-content + padding: 0 5px 5px + +#modal-header text-align: right + padding-right: 5px -#popover-close-button +#modal-close-button color: color('primary') &:hover color: color('error') - -.popover-form - margin-bottom: 0 diff --git a/assets/sass/_select_dropdown.sass b/assets/sass/_select_dropdown.sass index 16481a2a..f0cef59c 100644 --- a/assets/sass/_select_dropdown.sass +++ b/assets/sass/_select_dropdown.sass @@ -45,3 +45,8 @@ top: 4px right: 5px cursor: pointer + .select-loading-icon + color: color('medium') + position: absolute + top: 4px + right: 5px
\ No newline at end of file diff --git a/assets/sass/_task_form.sass b/assets/sass/_task_form.sass new file mode 100644 index 00000000..b7583981 --- /dev/null +++ b/assets/sass/_task_form.sass @@ -0,0 +1,32 @@ +@import variables + +.task-form-container + @include grid(100) + +.task-form-main-column + @include grid_width(60/100) + @include custom-device(1000px) + @include grid_width(1) + + input[type="text"] + width: 700px + max-width: 99% + +.task-form-secondary-column + max-width: 250px + min-width: 200px + max-height: 600px + padding-left: 10px + overflow: auto + @include grid_width(20/100) + @include custom-device(1000px) + @include grid_width(1) + max-width: 99% + max-height: none + label:first-child + margin-top: 0 + @include custom-device(1000px) + margin-top: 10px + +.task-form-bottom + @include grid_width(1) diff --git a/assets/sass/app.sass b/assets/sass/app.sass index 378b3ea3..003d940a 100644 --- a/assets/sass/app.sass +++ b/assets/sass/app.sass @@ -15,7 +15,7 @@ @import select_dropdown @import suggest_menu @import dialog_box -@import popover +@import modal @import pagination @import header @import logo @@ -41,6 +41,7 @@ @import task_board_date @import task_tags @import task_summary +@import task_form @import task_listing @import comment @import subtasks |