From e6bf603edc7245698f6d8359db3102b6df88ac2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A1=D0=B5=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=9C?=
=?UTF-8?q?=D0=B0=D1=80=D0=B8=D1=8F?=
Date: Mon, 18 Mar 2019 23:31:33 +0400
Subject: [PATCH 1/8] #33 url in paper
---
src/main/java/ru/ulstu/paper/model/Paper.java | 32 +++++++------------
.../java/ru/ulstu/paper/model/PaperDto.java | 12 +++++++
.../ru/ulstu/paper/service/PaperService.java | 13 ++------
.../db/changelog-20190318_000001-schema.xml | 10 ++++++
src/main/resources/db/changelog-master.xml | 1 +
.../fragments/paperDashboardFragment.html | 8 ++++-
.../resources/templates/papers/paper.html | 7 ++++
7 files changed, 52 insertions(+), 31 deletions(-)
create mode 100644 src/main/resources/db/changelog-20190318_000001-schema.xml
diff --git a/src/main/java/ru/ulstu/paper/model/Paper.java b/src/main/java/ru/ulstu/paper/model/Paper.java
index c09fd28..fce6817 100644
--- a/src/main/java/ru/ulstu/paper/model/Paper.java
+++ b/src/main/java/ru/ulstu/paper/model/Paper.java
@@ -9,26 +9,8 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.file.model.FileData;
import ru.ulstu.user.model.User;
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
-import javax.persistence.OneToMany;
-import javax.persistence.OrderBy;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+import javax.persistence.*;
+import java.util.*;
@Entity
public class Paper extends BaseEntity implements UserContainer {
@@ -75,6 +57,8 @@ public class Paper extends BaseEntity implements UserContainer {
private String comment;
+ private String url;
+
private Boolean locked = false;
@ManyToOne
@@ -156,6 +140,14 @@ public class Paper extends BaseEntity implements UserContainer {
this.authors = authors;
}
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
@Override
public Set getUsers() {
return getAuthors();
diff --git a/src/main/java/ru/ulstu/paper/model/PaperDto.java b/src/main/java/ru/ulstu/paper/model/PaperDto.java
index 3405768..7c6eeb7 100644
--- a/src/main/java/ru/ulstu/paper/model/PaperDto.java
+++ b/src/main/java/ru/ulstu/paper/model/PaperDto.java
@@ -29,6 +29,7 @@ public class PaperDto {
@NotEmpty
private List deadlines = new ArrayList<>();
private String comment;
+ private String url;
private Boolean locked;
private String tmpFileName;
private Integer fileId;
@@ -50,6 +51,7 @@ public class PaperDto {
@JsonProperty("updateDate") Date updateDate,
@JsonProperty("deadlines") List deadlines,
@JsonProperty("comment") String comment,
+ @JsonProperty("url") String url,
@JsonProperty("locked") Boolean locked,
@JsonProperty("tmpFileName") String tmpFileName,
@JsonProperty("authorIds") Set authorIds,
@@ -61,6 +63,7 @@ public class PaperDto {
this.updateDate = updateDate;
this.deadlines = deadlines;
this.comment = comment;
+ this.url = url;
this.locked = locked;
this.tmpFileName = tmpFileName;
this.fileId = null;
@@ -77,6 +80,7 @@ public class PaperDto {
this.updateDate = paper.getUpdateDate();
this.deadlines = paper.getDeadlines();
this.comment = paper.getComment();
+ this.url = paper.getUrl();
this.locked = paper.getLocked();
this.tmpFileName = null;
this.fileId = paper.getFileData() == null ? null : paper.getFileData().getId();
@@ -198,6 +202,14 @@ public class PaperDto {
this.authorIds = authorIds;
}
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
public String getAuthorsString() {
return StringUtils.abbreviate(authors
.stream()
diff --git a/src/main/java/ru/ulstu/paper/service/PaperService.java b/src/main/java/ru/ulstu/paper/service/PaperService.java
index 9df8be2..49102d6 100644
--- a/src/main/java/ru/ulstu/paper/service/PaperService.java
+++ b/src/main/java/ru/ulstu/paper/service/PaperService.java
@@ -14,20 +14,12 @@ import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
import java.util.stream.Collectors;
import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert;
-import static ru.ulstu.paper.model.Paper.PaperStatus.ATTENTION;
-import static ru.ulstu.paper.model.Paper.PaperStatus.COMPLETED;
-import static ru.ulstu.paper.model.Paper.PaperStatus.DRAFT;
-import static ru.ulstu.paper.model.Paper.PaperStatus.FAILED;
-import static ru.ulstu.paper.model.Paper.PaperStatus.ON_PREPARATION;
+import static ru.ulstu.paper.model.Paper.PaperStatus.*;
@Service
public class PaperService {
@@ -82,6 +74,7 @@ public class PaperService {
private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException {
paper.setComment(paperDto.getComment());
+ paper.setUrl(paperDto.getUrl());
paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate());
paper.setLocked(paperDto.getLocked());
paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus());
diff --git a/src/main/resources/db/changelog-20190318_000001-schema.xml b/src/main/resources/db/changelog-20190318_000001-schema.xml
new file mode 100644
index 0000000..98ec657
--- /dev/null
+++ b/src/main/resources/db/changelog-20190318_000001-schema.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml
index b8654ae..1276108 100644
--- a/src/main/resources/db/changelog-master.xml
+++ b/src/main/resources/db/changelog-master.xml
@@ -18,5 +18,6 @@
+
\ No newline at end of file
diff --git a/src/main/resources/templates/papers/fragments/paperDashboardFragment.html b/src/main/resources/templates/papers/fragments/paperDashboardFragment.html
index 122696e..f55d724 100644
--- a/src/main/resources/templates/papers/fragments/paperDashboardFragment.html
+++ b/src/main/resources/templates/papers/fragments/paperDashboardFragment.html
@@ -10,7 +10,13 @@
-
title
+
+
+
+ title
+
authors
diff --git a/src/main/resources/templates/papers/paper.html b/src/main/resources/templates/papers/paper.html
index a5ff6b5..a01e858 100644
--- a/src/main/resources/templates/papers/paper.html
+++ b/src/main/resources/templates/papers/paper.html
@@ -50,6 +50,13 @@
th:field="*{comment}">
+
+ Ссылка на сайт конференции:
+
+
+
diff --git a/src/main/resources/templates/grants/grants.html b/src/main/resources/templates/grants/grants.html
index 83083a0..d98f6d8 100644
--- a/src/main/resources/templates/grants/grants.html
+++ b/src/main/resources/templates/grants/grants.html
@@ -24,6 +24,7 @@
+
diff --git a/src/main/resources/templates/papers/dashboard.html b/src/main/resources/templates/papers/dashboard.html
index b7849f7..5265e0b 100644
--- a/src/main/resources/templates/papers/dashboard.html
+++ b/src/main/resources/templates/papers/dashboard.html
@@ -18,6 +18,7 @@
+
diff --git a/src/main/resources/templates/papers/papers.html b/src/main/resources/templates/papers/papers.html
index a797d26..7a033fe 100644
--- a/src/main/resources/templates/papers/papers.html
+++ b/src/main/resources/templates/papers/papers.html
@@ -43,6 +43,7 @@
+
From 7b0b7a041af65ebb089665cebb3b65eec4faedeb Mon Sep 17 00:00:00 2001
From: Anton Romanov
Date: Thu, 28 Mar 2019 14:45:39 +0400
Subject: [PATCH 6/8] #80 hide mvc controllers from api
---
src/main/java/ru/ulstu/grant/controller/GrantController.java | 2 ++
src/main/java/ru/ulstu/index/controller/IndexController.java | 2 ++
src/main/java/ru/ulstu/paper/controller/PaperController.java | 2 ++
3 files changed, 6 insertions(+)
diff --git a/src/main/java/ru/ulstu/grant/controller/GrantController.java b/src/main/java/ru/ulstu/grant/controller/GrantController.java
index c786ffa..65b11dc 100644
--- a/src/main/java/ru/ulstu/grant/controller/GrantController.java
+++ b/src/main/java/ru/ulstu/grant/controller/GrantController.java
@@ -13,6 +13,7 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.Grant;
import ru.ulstu.grant.model.GrantDto;
import ru.ulstu.grant.service.GrantService;
+import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
@@ -28,6 +29,7 @@ import static ru.ulstu.grant.controller.Navigation.hasErrors;
@Controller()
@RequestMapping(value = "/grants")
+@ApiIgnore
public class GrantController {
private final GrantService grantService;
diff --git a/src/main/java/ru/ulstu/index/controller/IndexController.java b/src/main/java/ru/ulstu/index/controller/IndexController.java
index d964997..f6ab100 100644
--- a/src/main/java/ru/ulstu/index/controller/IndexController.java
+++ b/src/main/java/ru/ulstu/index/controller/IndexController.java
@@ -6,9 +6,11 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.ulstu.core.controller.AdviceController;
import ru.ulstu.user.service.UserService;
+import springfox.documentation.annotations.ApiIgnore;
@Controller()
@RequestMapping(value = "/index")
+@ApiIgnore
public class IndexController extends AdviceController {
public IndexController(UserService userService) {
super(userService);
diff --git a/src/main/java/ru/ulstu/paper/controller/PaperController.java b/src/main/java/ru/ulstu/paper/controller/PaperController.java
index 31df0e3..9169bd9 100644
--- a/src/main/java/ru/ulstu/paper/controller/PaperController.java
+++ b/src/main/java/ru/ulstu/paper/controller/PaperController.java
@@ -15,6 +15,7 @@ import ru.ulstu.paper.model.PaperDto;
import ru.ulstu.paper.model.PaperFilterDto;
import ru.ulstu.paper.service.PaperService;
import ru.ulstu.user.model.User;
+import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.io.IOException;
@@ -28,6 +29,7 @@ import static org.springframework.util.StringUtils.isEmpty;
@Controller()
@RequestMapping(value = "/papers")
+@ApiIgnore
public class PaperController {
private final PaperService paperService;
From 539095c89e11ff79213f2f537c34ce812b06ce3f Mon Sep 17 00:00:00 2001
From: Anton Romanov
Date: Thu, 28 Mar 2019 14:59:35 +0400
Subject: [PATCH 7/8] partially restored commits page
---
.../ulstu/configuration/MvcConfiguration.java | 2 +-
src/main/resources/public/css/odin.css | 226 +++
src/main/resources/public/js/config.js | 10 +
src/main/resources/public/js/core.js | 6 +-
src/main/resources/public/js/odin.js | 1512 +++++++++++++++++
src/main/resources/public/templates/odin.html | 165 ++
src/main/resources/templates/default.html | 5 +
7 files changed, 1922 insertions(+), 4 deletions(-)
create mode 100644 src/main/resources/public/css/odin.css
create mode 100644 src/main/resources/public/js/config.js
create mode 100644 src/main/resources/public/js/odin.js
create mode 100644 src/main/resources/public/templates/odin.html
diff --git a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java
index 3e8d66f..c704596 100644
--- a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java
+++ b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java
@@ -10,7 +10,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{articlename:\\w+}");
- //registry.addViewController("/admin/{articlename:\\w+}");
+ registry.addViewController("/admin/{articlename:\\w+}");
registry.addViewController("/papers/{articlename:\\w+}");
registry.addViewController("/grants/{articlename:\\w+}");
registry.addViewController("/conferences/{articlename:\\w+}");
diff --git a/src/main/resources/public/css/odin.css b/src/main/resources/public/css/odin.css
new file mode 100644
index 0000000..2bfeb75
--- /dev/null
+++ b/src/main/resources/public/css/odin.css
@@ -0,0 +1,226 @@
+.odin-unselectable {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.odin-kill-padding {
+ padding: 0;
+}
+
+.odin-input-group {
+ padding-left: 15px !important;
+ padding-right: 15px !important;
+}
+
+.odin-closable .fa {
+ font-size: 1.5em;
+ margin-top: -1px;
+}
+
+.odin-closable .fa:hover:before {
+ content: "\f057";
+}
+
+/*
+ Odin Toolbar
+*/
+.odin-toolbar {
+ padding-left: 1px;
+ padding-bottom: 4px;
+}
+
+.odin-toolbar .odin-btn {
+ float: none !important;
+}
+
+.odin-btn {
+ min-width: 112px;
+ margin-right: 3px;
+}
+
+@media (min-width: 768px) {
+ .odin-btn {
+ min-width: 150px;
+ }
+}
+
+/*
+ Odin Paginator
+*/
+.odin-paginator {
+ margin: 0;
+ margin-top: 5px;
+ text-align: center;
+}
+
+.odin-paginator-content {
+ display: inline-block;
+}
+
+.odin-paginator-content a {
+ cursor: pointer;
+ color: black;
+ float: left;
+ padding: 6px 16px;
+ text-decoration: none;
+ transition: background-color .3s;
+ border-radius: 4px;
+}
+
+.odin-paginator-content i {
+ color: black;
+ float: left;
+ padding: 6px 16px;
+}
+
+.odin-paginator-content a.active {
+ background-color: #4CAF50;
+ color: white;
+}
+
+.odin-paginator-content a:hover:not(.active) {
+ background-color: #ddd;
+}
+
+/*
+ Odin Formatters
+*/
+.odin-negative {
+ color: red;
+ font-weight: bold;
+}
+
+/*
+ Odin Table
+*/
+.odin-table {
+ min-height: 324px;
+ border: 1px solid #ddd;
+ padding: 0;
+ margin: 0 0 0 1px;
+ background-color: #f8f8f8;
+}
+
+.odin-table > table {
+ margin-bottom: 0;
+}
+
+.odin-table > table > tbody > tr {
+ background-color: #fff;
+}
+
+.odin-table-pointed-line {
+ cursor: pointer;
+}
+
+.odin-table-selected-line {
+ background-color: #5bc0de !important;
+}
+
+.odin-table-selected-line:hover {
+ background-color: #6bd0ee !important;
+}
+
+/*
+ Odin Form
+ */
+.odin-form {
+ display: none;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ z-index: 1500;
+ background: rgba(0, 0, 0, 0.5);
+}
+
+.odin-form .panel {
+ position: relative;
+ max-height: 95%;
+ max-width: 95%;
+ overflow: auto;
+}
+
+@media (min-width: 768px) {
+ .odin-form .panel {
+ max-width: 55%;
+ }
+}
+
+.odin-form .panel-footer {
+ padding: 5px 7px;
+}
+
+.odin-form .odin-btn {
+ float: right;
+}
+
+.odin-checkbox {
+ width: 20px;
+ height: 20px;
+}
+
+.odin-form .tab-pane {
+ padding-top: 5px;
+}
+
+/*
+ Odin Confirm Box
+ */
+.odin-confirm-box {
+ display: none;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ z-index: 2000;
+ background: rgba(0, 0, 0, 0.5);
+}
+
+.odin-confirm-box .panel {
+ position: relative;
+ max-width: 95%;
+}
+
+.odin-confirm-box .panel-footer {
+ text-align: center;
+}
+
+@media (min-width: 768px) {
+ .odin-confirm-box .panel {
+ max-width: 25%;
+ }
+}
+
+.odin-confirm-box .panel-body {
+ text-align: center;
+}
+
+/*
+ Odin Table Box
+ */
+.odin-table-box {
+ z-index: 2500;
+}
+
+/*
+ Odin Simple Box
+ */
+
+@media (min-width: 768px) {
+ .odin-simple-form .panel {
+ max-width: 35%;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/public/js/config.js b/src/main/resources/public/js/config.js
new file mode 100644
index 0000000..353180b
--- /dev/null
+++ b/src/main/resources/public/js/config.js
@@ -0,0 +1,10 @@
+/* exported contextPath */
+var contextPath = "";
+var apiVersion = "/api/1.0";
+
+var basePath = contextPath + apiVersion;
+
+/* exported urlVersions */
+var urlVersions = basePath + "/versions";
+/* exported urlCommits */
+var urlCommits = basePath + "/commits";
\ No newline at end of file
diff --git a/src/main/resources/public/js/core.js b/src/main/resources/public/js/core.js
index 216f411..2d6ff62 100644
--- a/src/main/resources/public/js/core.js
+++ b/src/main/resources/public/js/core.js
@@ -88,12 +88,12 @@ function getFromRest(url, callBack, errorCallBack) {
}
/* exported getFromRestWithVersion */
-function getFromRestWithVersion(url, callBack, errorCallBack) {
- getFromRestWithVersionAndParams(url, "", callBack, errorCallBack);
+function getFromRest(url, callBack, errorCallBack) {
+ getFromRestWithParams(url, "", callBack, errorCallBack);
}
/* exported getFromRestWithVersionAndParams */
-function getFromRestWithVersionAndParams(url, params, callBack, errorCallBack) {
+function getFromRestWithParams(url, params, callBack, errorCallBack) {
getCurrentVersion(function (version) {
$.ajax({
url: url + "?versionId=" + version + params,
diff --git a/src/main/resources/public/js/odin.js b/src/main/resources/public/js/odin.js
new file mode 100644
index 0000000..384c496
--- /dev/null
+++ b/src/main/resources/public/js/odin.js
@@ -0,0 +1,1512 @@
+// form config.js
+/* global uiLocale, urlVersions */
+// from core.js
+/* global isEmpty, deleteFromRest, fillSelect, MessageTypesEnum, showFeedbackMessage, getCurrentVersionData */
+/* global getFromRestWithVersionAndParams, getFromRestWithVersion, getFromRest */
+/* global postToRestWithVersionAndParams, putToRestWithVersionAndParams */
+
+// TODO: settings for table selection mode (single, multiple)
+
+var odinMetaList = "/meta/list";
+var odinMetaElement = "/meta/element";
+var odinCopy = "/copy-from-version";
+
+/* exported OdinTableWithMeta */
+function OdinTableWithMeta(divId, dataUrl, disableCopy) {
+ return OdinTableWithMetaAndParams(divId, dataUrl, "", disableCopy, "");
+}
+
+/* exported OdinTableWithMetaAndGlobalCallback */
+function OdinTableWithMetaAndGlobalCallback(divId, dataUrl, disableCopy, globalSaveCallback, globalSelectCallback) {
+ return OdinTableWithMetaAndParams(divId, dataUrl, "", disableCopy, globalSaveCallback, globalSelectCallback);
+}
+
+/* exported OdinTableWithMetaAndParams */
+function OdinTableWithMetaAndParams(divId, dataUrl, params, disableCopy, globalSaveCallback, globalSelectCallback) {
+ return new OdinTable(divId, dataUrl + odinMetaList, dataUrl + odinMetaElement, dataUrl,
+ params, disableCopy, globalSaveCallback, globalSelectCallback);
+}
+
+function OdinTable(divId, listMetaDataUrl, elementMetaDataUrl, dataUrl, params, disableCopy, globalSaveCallback, globalSelectCallback) {
+ var CLICK_DELAY = 250;
+ var DATA_ATTR = "odin-data";
+ var DATA_VIEW_ATTR = "odin-data-view";
+ var TYPE_ATTR = "type";
+ var ID_ATTR = "id";
+ var DISABLED_ATTR = "disabled";
+ var REQUIRED_ATTR = "required";
+ var LABELS_SELECTOR = "label";
+ var INPUTS_SELECTOR = ":input";
+ var BUTTONS_SELECTOR = ":button";
+ var CHECKED_ATTR = "checked";
+ var NOT_CHECKED_ATTR = "";
+ var PAGINATOR_ITEMS_PER_PAGE = 10;
+ var PAGINATOR_VISIBLE_BUTTONS_COUNT = 3;
+ var odinTable;
+
+ var L10nEnum = {
+ VIEW: "Наименование",
+ COLLECTION: "Коллеция объектов",
+ GO_TO: "Перейти",
+
+ BUTTON_ADD: "Добавить",
+ BUTTON_CREATE: "Создать",
+ BUTTON_EDIT: "Изменить",
+ BUTTON_DELETE: "Удалить",
+
+ BUTTON_COPY: "Копировать",
+
+ BUTTON_OK: "Продолжить",
+ BUTTON_CANCEL: "Отмена",
+
+ BUTTON_SAVE: "Записать",
+ BUTTON_CLOSE: "Закрыть",
+
+ CAPTION_CREATE_FORM: "Новый элемент",
+ CAPTION_EDIT_FORM: "(редактирование)",
+ CAPTION_CHOOSE_FORM: "Выбор объекта",
+ CAPTION_COPY_FORM: "Копирование данных",
+ CAPTION_DEFAULT_TAB: "Реквизиты",
+ CPATION_FORM_DEFAULT: "...",
+
+ QUESTION_LOST_CHANGES: "Закрыть без сохранения?",
+ QUESTION_DELETE_ITEM: "Выполнить удаление?",
+ QUESTION_DEFAULT_CAPTION: "Продолжит?",
+
+ ERROR_VERSIONS_MATCH: "Версии совпадают"
+ };
+ Object.freeze(L10nEnum);
+
+ var FieldTypesEnum = {
+ BOOLEAN: "boolean",
+ DATE: "date",
+ NUMERIC: "numeric",
+ STRING: "string",
+ COLLECTION: "collection",
+ OBJECT: "object"
+ };
+ Object.freeze(FieldTypesEnum);
+
+ var DateTypesEnum = {
+ DATETIME: "datetime",
+ DATE: "date",
+ TIME: "time"
+ };
+ Object.freeze(DateTypesEnum);
+
+ var StringTypesEnum = {
+ STRING: "string",
+ PASSWORD: "password",
+ TEXT: "text",
+ EMAIL: "email",
+ HREF: "href"
+ };
+ Object.freeze(StringTypesEnum);
+
+ var VisibleTypesEnum = {
+ ALL: "all",
+ ON_CREATE: "on_create",
+ ON_UPDATE: "on_update",
+ NONE: "none"
+ };
+ Object.freeze(VisibleTypesEnum);
+
+ var InputTypesEnum = {
+ CHECKBOX: "checkbox",
+ PASSWORD: "password",
+ TEXT: "text",
+ EMAIL: "email",
+ DATE: "date",
+ DATETIME: "datetime-local",
+ TIME: "time",
+ NUMBER: "number",
+ HIDDEN: "hidden",
+ SUBMIT: "submit"
+ };
+ Object.freeze(InputTypesEnum);
+
+ var FormTypesEnum = {
+ FORM: "form",
+ CONFIRM: "confirm",
+ SIMPLE: "simple"
+ };
+ Object.freeze(FormTypesEnum);
+
+ var NumberAttrEnum = {
+ MIN: "min",
+ MAX: "max",
+ STEP: "step"
+ };
+ Object.freeze(NumberAttrEnum);
+
+ var StringAttrEnum = {
+ MIN: "minlength",
+ MAX: "maxlength"
+ };
+ Object.freeze(StringAttrEnum);
+
+ $(document.body).append("
");
+ $("#odinTemplates").load("/templates/odin.html", function () {
+ $("#" + divId).append("" +
+ " " +
+ "
");
+ odinTable = new OdinTableMain(divId, listMetaDataUrl, elementMetaDataUrl, dataUrl, params, disableCopy, globalSaveCallback, globalSelectCallback);
+ });
+
+ this.getSelectedItems = function () {
+ return odinTable.getSelectedItems();
+ }
+
+ function format(str, args) {
+ if (isEmpty(str) || isEmpty(args)) {
+ return str;
+ }
+ if (!Array.isArray(args)) {
+ throw "Odin: Format args is not array";
+ }
+ return str.replace(/\{(\d+)\}/g,
+ function (match, number) {
+ return typeof args[number] !== "undefined" ?
+ args[number] : match;
+ });
+ }
+
+ function centerDiv(component) {
+ if (isEmpty(component)) {
+ return;
+ }
+ component.offset({
+ top: ($(window).height() - component.height()) / 2,
+ left: ($(window).width() - component.width()) / 2
+ });
+ }
+
+ function tmpl(id) {
+ var html = $("#odinTemplates").find("#" + id).html();
+ if (isEmpty(html)) {
+ throw "Odin: Template load error";
+ }
+ var template = format(html.trim(), Array.prototype.slice.call(arguments, 1));
+ return $(template);
+ }
+
+ function find(object, selector) {
+ var result = object.find(selector);
+ if (isEmpty(result)) {
+ throw "Can't find elements by selector " + selector;
+ }
+ return result;
+ }
+
+ function findFiltered(object, selector, notSelector) {
+ var result = object.find(selector).not(notSelector);
+ if (isEmpty(result)) {
+ throw "Can't find elements by selector " + selector;
+ }
+ return result;
+ }
+
+ function OdinFormCommon(type) {
+ var BUTTON_CLOSE_SELECTOR = "i.fa";
+ var CAPTION_SELECTOR = "#caption";
+ var PANEL_SELECTOR = ".panel";
+ var FORM_INPUTS_SELECTOR = ":input";
+ var DATA_CHANGED_ATTR = "changed";
+
+ var form;
+ var header;
+ var content;
+ var footer;
+
+ var createForm = function () {
+ switch (type) {
+ case FormTypesEnum.FORM:
+ form = tmpl("form");
+ break;
+ case FormTypesEnum.CONFIRM:
+ form = tmpl("formConfirm");
+ break;
+ default:
+ form = tmpl("formSimple");
+ }
+ if (isEmpty(form)) {
+ throw "Odin: Unknown form type";
+ }
+ header = createFormHeader();
+ content = createFormContent();
+ footer = createFormFooter();
+ var formPanel = tmpl("formPanel");
+ formPanel.append(header);
+ formPanel.append(content);
+ formPanel.append(footer);
+ form.append(formPanel);
+ setCaption(L10nEnum.CPATION_FORM_DEFAULT);
+ $(document.body).append(form);
+ };
+
+ var createFormHeader = function () {
+ var fHeader = tmpl("formHeader");
+ fHeader.find(BUTTON_CLOSE_SELECTOR).click(function () {
+ hideForm();
+ });
+ return fHeader;
+ };
+
+ var createFormContent = function () {
+ return tmpl("formContent");
+ };
+
+ var createFormFooter = function () {
+ return tmpl("formFooter");
+ };
+
+ var setCaption = function (caption) {
+ if (isEmpty(caption)) {
+ throw "Odin: Caption is not set";
+ }
+ find(header, CAPTION_SELECTOR).text(caption);
+ };
+
+ var addFormClass = function (clazz) {
+ if (isEmpty(clazz)) {
+ throw "Odin: Additional class is not set";
+ }
+ form.addClass(clazz);
+ };
+
+ var isModified = function () {
+ return form.data(DATA_CHANGED_ATTR);
+ };
+
+ var setFormSubmit = function (callBack) {
+ if (isEmpty(callBack)) {
+ throw "Odin: Callback is not set";
+ }
+ form.submit(function () {
+ callBack();
+ return false;
+ });
+ };
+
+ var showForm = function () {
+ form.show(0, function () {
+ centerDiv(find(form, PANEL_SELECTOR));
+ });
+ if (type === FormTypesEnum.FORM) {
+ find(form, FORM_INPUTS_SELECTOR).change(function () {
+ form.data(DATA_CHANGED_ATTR, true);
+ });
+ }
+ };
+
+ var hideForm = function () {
+ var callBack = function () {
+ form.hide();
+ form.remove();
+ };
+ callBack();
+ };
+
+ this._getHeader = function () {
+ return header;
+ };
+
+ this._getContent = function () {
+ return content;
+ };
+
+ this._getFooter = function () {
+ return footer;
+ };
+
+ this._setCaption = function (caption) {
+ setCaption(caption);
+ };
+
+ this._setFormSubmit = function (callBack) {
+ setFormSubmit(callBack);
+ };
+
+ this._addFormClass = function (clazz) {
+ addFormClass(clazz);
+ };
+
+ this._isModified = function () {
+ return isModified();
+ };
+
+ this._show = function () {
+ showForm();
+ };
+
+ this._hide = function () {
+ hideForm();
+ };
+
+ createForm();
+ }
+
+ function OdinToolbar(tableDiv, createCallBack, editCallBack, deleteCallBack) {
+ var SELECTOR_ODIN_TABLE = ".odin-table";
+
+ var toolbar;
+
+ var createButton;
+ var editButton;
+ var deleteButton;
+
+ var initialize = function () {
+ if (isEmpty(tableDiv)) {
+ throw "Odin-Toolbar: Target div is not set";
+ }
+ var table = find(tableDiv, SELECTOR_ODIN_TABLE);
+ toolbar = tmpl("toolbar");
+ toolbar.insertBefore(table, null);
+ if (!isEmpty(createCallBack)) {
+ createButton = tmpl("createButton", L10nEnum.BUTTON_CREATE);
+ createButton.click(function () {
+ createCallBack();
+ });
+ toolbar.append(createButton);
+ }
+ if (!isEmpty(editCallBack)) {
+ editButton = tmpl("editButton", L10nEnum.BUTTON_EDIT);
+ editButton.click(function () {
+ editCallBack();
+ });
+ setEditButtonState(false);
+ toolbar.append(editButton);
+ }
+ if (!isEmpty(deleteCallBack)) {
+ deleteButton = tmpl("deleteButton", L10nEnum.BUTTON_DELETE);
+ deleteButton.click(function () {
+ deleteCallBack();
+ });
+ setDeleteButtonState(false);
+ toolbar.append(deleteButton);
+ }
+ };
+
+ var setEditButtonState = function (state) {
+ if (isEmpty(editButton)) {
+ return;
+ }
+ editButton.attr("disabled", !state);
+ };
+
+ var setDeleteButtonState = function (state) {
+ if (isEmpty(deleteButton)) {
+ return;
+ }
+ deleteButton.attr("disabled", !state);
+ };
+
+ this.addButton = function (caption, callBack) {
+ if (isEmpty(caption) || isEmpty(callBack)) {
+ throw "Odin-Toolbar: Caption or callback is not set";
+ }
+ var button = tmpl("basicButton", caption);
+ button.click(function () {
+ callBack();
+ });
+ toolbar.append(button);
+ };
+
+ this.setEditButtonState = function (state) {
+ setEditButtonState(state);
+ };
+
+ this.setDeleteButtonState = function (state) {
+ setDeleteButtonState(state);
+ };
+
+ initialize();
+ }
+
+ function OdinPaginator(tableDiv, callBack) {
+ var SELECTOR_PAGINATOR_CONTENT = ".odin-paginator-content";
+ var SELECTOR_PAGINATOR_BUTTON = "a";
+ var PAGINATOR_BUTTON_PREFIX = "odin-paginator-";
+ var PAGINATOR_SELECTED_BUTTON_CLASS = "active";
+
+ var initialize = function () {
+ if (isEmpty(tableDiv)) {
+ throw "Odin-Paginator: Target div is not set";
+ }
+ if (isEmpty(callBack)) {
+ throw "Odin-Paginator: Callback is not set";
+ }
+ tableDiv.append(tmpl("paginator"));
+ };
+
+ var drawPaginator = function (data, offset) {
+ var paginatorContentDiv = find(tableDiv, SELECTOR_PAGINATOR_CONTENT);
+ paginatorContentDiv.empty();
+ if (isEmpty(data)) {
+ return;
+ }
+ $.each(data, function (index, value) {
+ if (value.name) {
+ paginatorContentDiv.append(tmpl("paginatorButton", value.offset, value.name));
+ } else {
+ paginatorContentDiv.append(tmpl("paginatorSeparator"));
+ }
+ });
+ var buttons = find(paginatorContentDiv, SELECTOR_PAGINATOR_BUTTON);
+ buttons.removeClass("active");
+ buttons.click(function () {
+ $('#preloader').show();
+ var offset = $(this).attr(ID_ATTR).replace(PAGINATOR_BUTTON_PREFIX, "");
+ callBack(offset);
+ });
+ find(paginatorContentDiv, "#" + PAGINATOR_BUTTON_PREFIX + offset)
+ .addClass(PAGINATOR_SELECTED_BUTTON_CLASS);
+ };
+
+ this.drawPaginator = function (offset, count) {
+ var currentOffset = parseInt(offset ? offset : "0");
+ var buttonCount = Math.floor(
+ count <= PAGINATOR_ITEMS_PER_PAGE ? 0 : count / PAGINATOR_ITEMS_PER_PAGE) +
+ (count < PAGINATOR_ITEMS_PER_PAGE ||
+ count % PAGINATOR_ITEMS_PER_PAGE === 0 ? 0 : 1);
+ var data = [];
+ Array.apply(0, Array(buttonCount)).forEach(function (element, index) {
+ if (index > 0 && index < buttonCount - 1 &&
+ buttonCount > PAGINATOR_VISIBLE_BUTTONS_COUNT * 2) {
+ if (index === currentOffset - PAGINATOR_VISIBLE_BUTTONS_COUNT ||
+ index === currentOffset + PAGINATOR_VISIBLE_BUTTONS_COUNT + 1) {
+ data.push({
+ "offset": null,
+ "name": null
+ });
+ }
+ if (index <= currentOffset - PAGINATOR_VISIBLE_BUTTONS_COUNT ||
+ index > currentOffset + PAGINATOR_VISIBLE_BUTTONS_COUNT) {
+ return true;
+ }
+ }
+ data.push({
+ "offset": index,
+ "name": index + 1
+ });
+ });
+ drawPaginator(data, currentOffset);
+ };
+
+ initialize();
+ }
+
+ function OdinTableFormatter() {
+ var BOOLEAN_TRUE_CLASS = "fa-check";
+ var BOOLEAN_FALSE_CLASS = "fa-times";
+
+ var formatBooleanValue = function (value) {
+ return tmpl("tableBoolean", (value ? BOOLEAN_TRUE_CLASS : BOOLEAN_FALSE_CLASS));
+ };
+
+ var formatDateValue = function (value, dateType) {
+ var date = new Date(value);
+ var formatterSettings = {};
+ switch (dateType) {
+ case DateTypesEnum.DATETIME:
+ formatterSettings = {
+ year: "numeric",
+ month: "numeric",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric"
+ };
+ break;
+ case DateTypesEnum.TIME:
+ formatterSettings = {
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric"
+ };
+ break;
+ }
+ var dateFormatter = new Intl.DateTimeFormat(uiLocale, formatterSettings);
+ return dateFormatter.format(date);
+ };
+
+ var formatNumericValue = function (value, positiveOnly, scale) {
+ var numberFormatter = new Intl.NumberFormat(uiLocale, {
+ useGrouping: false,
+ minimumFractionDigits: scale
+ });
+ var formattedValue = numberFormatter.format(value);
+ if (positiveOnly && value < 0) {
+ return tmpl("tableNumericNegative", formattedValue);
+ }
+ return formattedValue;
+ };
+
+ var formatStringValue = function (value, maxLength, stringType) {
+ switch (stringType) {
+ case StringTypesEnum.HREF:
+ return tmpl("anchor", value, L10nEnum.GO_TO);
+ default:
+ if (isEmpty(maxLength) || maxLength === 0) {
+ return value;
+ }
+ return value.substring(0, Math.min(value.length, maxLength));
+ }
+ };
+
+ this.formatValue = function (value, fieldParams) {
+ if (isEmpty(value) || isEmpty(fieldParams)) {
+ return "";
+ }
+ if (fieldParams.hidden) {
+ return value;
+ }
+ switch (fieldParams.fieldType) {
+ case FieldTypesEnum.BOOLEAN:
+ return formatBooleanValue(value);
+ case FieldTypesEnum.DATE:
+ return formatDateValue(value, fieldParams.type);
+ case FieldTypesEnum.NUMERIC:
+ return formatNumericValue(value, fieldParams.positiveOnly, fieldParams.scale);
+ case FieldTypesEnum.STRING:
+ return formatStringValue(value, fieldParams.maxLength, fieldParams.type);
+ case FieldTypesEnum.COLLECTION:
+ return L10nEnum.COLLECTION;
+ case FieldTypesEnum.OBJECT:
+ return value.view;
+ default:
+ return value;
+ }
+ };
+ }
+
+ function OdinTableView(odinDiv, editCallback, globalSelectCallback) {
+ var TABLEVIEW_SELECTED_LINE_CLASS = "odin-table-selected-line";
+ var TABLEVIEW_LINE_SELECTOR = "tr";
+ var TABLEVIEW_SELECTED_LINE_SELECTOR = TABLEVIEW_LINE_SELECTOR +
+ "." + TABLEVIEW_SELECTED_LINE_CLASS;
+ var TABLEVIEW_LINE_DATA_SELECTOR = TABLEVIEW_LINE_SELECTOR +
+ "[" + DATA_ATTR + "=\"{0}\"]";
+ var TABLEVIEW_CHOOSABLE_CLASS = "table-hover odin-unselectable";
+ var TABLEVIEW_LINE_POINTED_CLASS = "odin-table-pointed-line";
+ var TABLEVIEW_HEAD_COLUMN_SELECTOR = "th:nth-child({0})";
+ var TABLEVIEW_BODY_COLUMN_SELECTOR = "td:nth-child({0})";
+
+ var tTable;
+ var tHead;
+ var tBody;
+ var choosable;
+ var formatter;
+ var toolbar;
+
+ var createLine = function (idValue) {
+ var prevent = false;
+ var timer = 0;
+ var newLine = tmpl("tableLine");
+ if (!isEmpty(idValue) && choosable) {
+ newLine.attr(DATA_ATTR, idValue);
+ newLine.click(function () {
+ var btn = $(this);
+ timer = setTimeout(function () {
+ if (!prevent) {
+ btn.toggleClass(TABLEVIEW_SELECTED_LINE_CLASS);
+ }
+ prevent = false;
+ if (!isEmpty(globalSelectCallback)) {
+ globalSelectCallback(getLines());
+ }
+ if (!isEmpty(toolbar)) {
+ toolbar.setEditButtonState(!isEmpty(getLines()));
+ toolbar.setDeleteButtonState(!isEmpty(getLines()));
+ }
+ }, CLICK_DELAY);
+ });
+ if (!isEmpty(editCallback)) {
+ newLine.dblclick(function () {
+ clearTimeout(timer);
+ prevent = true;
+ editCallback(idValue);
+ });
+ }
+ newLine.addClass(TABLEVIEW_LINE_POINTED_CLASS);
+ }
+ return newLine;
+ };
+
+ var addLineToHead = function (line) {
+ tHead.append(line);
+ };
+
+ var addLineToBody = function (line) {
+ tBody.append(line);
+ };
+
+ var clearBody = function () {
+ tBody.empty();
+ };
+
+ var initialize = function () {
+ if (isEmpty(odinDiv)) {
+ throw "Odin: Odin div is not set";
+ }
+ odinDiv.append(tmpl("table"));
+ tTable = find(odinDiv, "table");
+ tHead = find(odinDiv, "thead");
+ tBody = find(odinDiv, "tbody");
+ formatter = new OdinTableFormatter();
+ };
+
+ var getLines = function (isAll) {
+ var selector = isAll ? TABLEVIEW_LINE_SELECTOR : TABLEVIEW_SELECTED_LINE_SELECTOR;
+ var selectedItems = [];
+ try {
+ find(tBody, selector).each(function () {
+ selectedItems.push({
+ id: $(this).attr(DATA_ATTR),
+ view: $(this).attr(DATA_VIEW_ATTR)
+ });
+ });
+ } catch (e) {
+ // Ignore
+ }
+ return selectedItems;
+ };
+
+ var disableToolbar = function () {
+ if (isEmpty(toolbar)) {
+ return;
+ }
+ toolbar.setEditButtonState(false);
+ toolbar.setDeleteButtonState(false);
+ };
+
+ this.createHead = function (metaData, isOdinDto) {
+ if (isEmpty(metaData)) {
+ throw "Odin: Metadata is not set";
+ }
+ choosable = isOdinDto;
+ if (choosable) {
+ tTable.addClass(TABLEVIEW_CHOOSABLE_CLASS);
+ }
+ var tHeadLine = createLine();
+ $.each(metaData, function (index, value) {
+ tHeadLine.append(tmpl("tableHeadColumn", value.caption));
+ });
+ addLineToHead(tHeadLine);
+ $.each(metaData, function (columnIndex, columnParams) {
+ if (columnParams.visible === VisibleTypesEnum.NONE) {
+ var index = columnIndex + 1;
+ find(tHead, format(TABLEVIEW_HEAD_COLUMN_SELECTOR, [index])).hide();
+ }
+ });
+ };
+
+ this.fillBody = function (metaData, data) {
+ clearBody();
+ $.each(data, function (index, value) {
+ var idValue = choosable ? value.id : null;
+ var line = createLine(idValue);
+ line.attr(DATA_VIEW_ATTR, value.view);
+ $.each(metaData, function (columnIndex, columnParams) {
+ var column = tmpl("tableBodyColumn");
+ column.append(
+ formatter.formatValue(value[columnParams.fieldName],
+ columnParams));
+ line.append(column);
+ });
+ addLineToBody(line);
+ });
+ if (!isEmpty(data)) {
+ $.each(metaData, function (columnIndex, columnParams) {
+ if (columnParams.visible === VisibleTypesEnum.NONE) {
+ var index = columnIndex + 1;
+ find(tBody, format(TABLEVIEW_BODY_COLUMN_SELECTOR, [index])).hide();
+ }
+ });
+ }
+ };
+
+ this.getSelectedItems = function () {
+ return getLines(false);
+ };
+
+ this.getItems = function () {
+ return getLines(true);
+ };
+
+ // Delete form view only!
+ this.deleteById = function (id) {
+ if (isEmpty(id)) {
+ return;
+ }
+ try {
+ find(tBody, format(TABLEVIEW_LINE_DATA_SELECTOR, [id])).remove();
+ disableToolbar();
+ } catch (e) {
+ // Ignore
+ }
+ };
+
+ this.setToolbar = function (toolbarVar) {
+ toolbar = toolbarVar;
+ };
+
+ this.disableToolbar = function () {
+ disableToolbar();
+ };
+
+ initialize();
+ }
+
+ function OdinTableMain(divId, listMetaDataUrl, elementMetaDataUrl, dataUrl, args, disableCopy, globalSaveCallback, globalSelectCallback) {
+ if (isEmpty(divId) || isEmpty(listMetaDataUrl) || isEmpty(dataUrl)) {
+ throw "Odin: Not enough parameters";
+ }
+
+ var OFFSET_ARG = "&offset=";
+ var COUNT_ARG = "&count=";
+
+ var odinDiv;
+ var tableView;
+ var paginator;
+
+ var currentOffset;
+
+ var listMetaData;
+ var elementMetaData;
+
+ var params = isEmpty(args) ? "" : args;
+
+ var drawBody = function (offset) {
+ if (isEmpty(listMetaData)) {
+ throw "Odin: Meta data is not defined";
+ }
+ currentOffset = isEmpty(offset) ? 0 : parseInt(offset);
+ getFromRestWithVersionAndParams(dataUrl,
+ OFFSET_ARG + (currentOffset * PAGINATOR_ITEMS_PER_PAGE) +
+ COUNT_ARG + PAGINATOR_ITEMS_PER_PAGE + params, function (data) {
+ if (isEmpty(data)) {
+ throw "Odin: Response data is null";
+ }
+ tableView.fillBody(listMetaData, data.items);
+ if (data.count > 0) {
+ paginator.drawPaginator(offset, data.count);
+ }
+ tableView.disableToolbar();
+ $('#preloader').hide();
+ });
+ };
+
+ var initialize = function () {
+ odinDiv = find($(document.body), "#" + divId);
+ tableView = new OdinTableView(odinDiv, editItem, globalSelectCallback);
+ paginator = new OdinPaginator(odinDiv, drawBody);
+ getFromRest(listMetaDataUrl, function (data) {
+ if (isEmpty(data) || isEmpty(data.fields)) {
+ throw "Odin: Can't load list meta data";
+ }
+ var isOdinDto = data.odinDto;
+ listMetaData = data.fields;
+ if (isOdinDto && !isEmpty(elementMetaDataUrl)) {
+ getFromRest(elementMetaDataUrl, function (data) {
+ if (isEmpty(data) || isEmpty(data.fields)) {
+ throw "Odin: Can't load element meta data";
+ }
+ elementMetaData = data.fields;
+ var toolbar = new OdinToolbar(odinDiv, createItem, editItem, deleteItem, globalSaveCallback);
+ tableView.setToolbar(toolbar);
+ if (!disableCopy) {
+ toolbar.addButton(L10nEnum.BUTTON_COPY, function () {
+ new OdinCopyBox(dataUrl, drawBody);
+ });
+ }
+ });
+ }
+ tableView.createHead(listMetaData, isOdinDto);
+ drawBody();
+ });
+ };
+
+ var updateTable = function () {
+ // TODO: recalculate offset before redraw table
+ drawBody(currentOffset);
+ };
+
+ var createItem = function () {
+ new OdinForm(elementMetaData, dataUrl, updateTable, null, params);
+ };
+
+ var editItem = function (value) {
+ if (isEmpty(elementMetaDataUrl)) {
+ return;
+ }
+ var curValue = value;
+ if (isEmpty(value)) {
+ var selectedItems = tableView.getSelectedItems();
+ if (!isEmpty(selectedItems)) {
+ curValue = selectedItems[0].id;
+ }
+ }
+ if (isEmpty(curValue)) {
+ return;
+ }
+ new OdinForm(elementMetaData, dataUrl, updateTable, curValue, params);
+ };
+
+ var deleteItem = function () {
+ var selectedItems = tableView.getSelectedItems();
+ if (isEmpty(selectedItems)) {
+ return;
+ }
+ var callBack = function () {
+ $.each(selectedItems, function (index, value) {
+ deleteFromRest(dataUrl + "/" + value.id, null, function () {
+ if (index === selectedItems.length - 1) {
+ updateTable();
+ }
+ });
+ });
+ };
+ new OdinConfirmBox(callBack, L10nEnum.QUESTION_DELETE_ITEM);
+ };
+
+ this.getSelectedItems = function () {
+ return tableView.getSelectedItems();
+ };
+
+ initialize();
+ }
+
+ function OdinFormControlGenerator() {
+ this.createInputGroup = function () {
+ return tmpl("formInputGroup");
+ };
+
+ this.createLabel = function (id, caption) {
+ return tmpl("formLabel", id, caption);
+ };
+
+ var createInput = function (id, value, type) {
+ if (isEmpty(id)) {
+ throw "Odin: Input id is not set";
+ }
+ if (isEmpty(type)) {
+ throw "Odin: Input type is not set";
+ }
+ var curValue = isEmpty(value) ? "" : value;
+ var wrapper = tmpl("formInputWrapper");
+ if (type === InputTypesEnum.CHECKBOX) {
+ wrapper.append(tmpl("formInputCheckbox",
+ id, (curValue ? CHECKED_ATTR : NOT_CHECKED_ATTR)));
+ } else {
+ wrapper.append(tmpl("formInput",
+ id, curValue, type));
+ }
+ return wrapper;
+ };
+
+ this.createBooleanInput = function (id, value) {
+ return createInput(id, value, InputTypesEnum.CHECKBOX);
+ };
+
+ var fixDateUnit = function (dateUnitValue) {
+ return dateUnitValue < 10 ? "0" + dateUnitValue : dateUnitValue;
+ };
+
+ var dateToStr = function (value, type) {
+ if (isEmpty(value)) {
+ return "";
+ }
+ var date = new Date(value);
+ var year = fixDateUnit(date.getFullYear());
+ var month = fixDateUnit(date.getMonth() + 1);
+ var day = fixDateUnit(date.getDate());
+ var hour = fixDateUnit(date.getHours());
+ var minute = fixDateUnit(date.getMinutes());
+ var formattedDate;
+ switch (type) {
+ case DateTypesEnum.DATETIME:
+ formattedDate = year + "-" + month + "-" + day + "T" + hour + ":" + minute;
+ break;
+ case DateTypesEnum.TIME:
+ formattedDate = hour + ":" + minute;
+ break;
+ default:
+ formattedDate = year + "-" + month + "-" + day;
+ }
+ return formattedDate;
+ };
+
+ this.createDateInput = function (id, value, type) {
+ var dateType;
+ switch (type) {
+ case DateTypesEnum.DATETIME:
+ dateType = InputTypesEnum.DATETIME;
+ break;
+ case DateTypesEnum.TIME:
+ dateType = InputTypesEnum.TIME;
+ break;
+ default:
+ dateType = InputTypesEnum.DATE;
+ }
+ return createInput(id, dateToStr(value, type), dateType);
+ };
+
+ this.createNumericInput = function (id, value) {
+ return createInput(id, value, InputTypesEnum.NUMBER);
+ };
+
+ this.createStringInput = function (id, value, type) {
+ var stringType;
+ switch (type) {
+ case StringTypesEnum.PASSWORD:
+ stringType = InputTypesEnum.PASSWORD;
+ break;
+ case StringTypesEnum.EMAIL:
+ stringType = InputTypesEnum.EMAIL;
+ break;
+ default:
+ stringType = InputTypesEnum.TEXT;
+ }
+ return createInput(id, value, stringType);
+ };
+
+ this.dateToStr = function (value, type) {
+ return dateToStr(value, type);
+ };
+ }
+
+ function OdinFormContentGenerator(createNavTabCallBack, isCreateOperation) {
+ var PRECISION_CHAR = "9";
+ var SCALE_PREFIX = "0.";
+ var SCALE_CHAR = "0";
+ var SCALE_POSTFIX = "1";
+ var OBJECT_INPUT_CLASS = "input-group odin-input-group";
+ var CREATE_BUTTON_TEXT_SELECTOR = ".btn-success > span";
+ var CHOOSE_BTN_SELECTOR = ".odin-choose-btn";
+ var CLEAR_BTN_SELECTOR = ".odin-clear-btn";
+ var CLEARABLE_INPUT_SELECTOR = ".form-control";
+
+ var controlGenerator = new OdinFormControlGenerator();
+
+ var createBooleanControl = function (id, caption, value) {
+ var group = controlGenerator.createInputGroup();
+ group.append(controlGenerator.createLabel(id, caption));
+ group.append(controlGenerator.createBooleanInput(id, value));
+ return group;
+ };
+
+ var createDateControl = function (id, caption, value, type) {
+ var group = controlGenerator.createInputGroup();
+ group.append(controlGenerator.createLabel(id, caption));
+ group.append(controlGenerator.createDateInput(id, value, type));
+ return group;
+ };
+
+ var createNumericControl = function (id, caption, value, positiveOnly, precision, scale) {
+ var group = controlGenerator.createInputGroup();
+ group.append(controlGenerator.createLabel(id, caption));
+ var control = controlGenerator.createNumericInput(id, value);
+ find(control, INPUTS_SELECTOR).each(function () {
+ var currentControl = $(this);
+ if (positiveOnly) {
+ currentControl.attr(NumberAttrEnum.MIN, 0);
+ }
+ if (precision) {
+ currentControl
+ .attr(NumberAttrEnum.MAX, Array(precision + 1).join(PRECISION_CHAR));
+ }
+ if (scale) {
+ var step = SCALE_PREFIX + Array(scale).join(SCALE_CHAR) + SCALE_POSTFIX;
+ currentControl.attr(NumberAttrEnum.STEP, step);
+ }
+ });
+ group.append(control);
+ return group;
+ };
+
+ var createStringControl = function (id, caption, value, minLength, maxLength, type) {
+ var group = controlGenerator.createInputGroup();
+ group.append(controlGenerator.createLabel(id, caption));
+ var control = controlGenerator.createStringInput(id, value, type);
+ find(control, INPUTS_SELECTOR).each(function () {
+ var currentControl = $(this);
+ currentControl.attr(StringAttrEnum.MIN, minLength);
+ currentControl.attr(StringAttrEnum.MAX, maxLength);
+ });
+ group.append(control);
+ return group;
+ };
+
+ var createCollectionControl = function (id, caption, values, path) {
+ // TODO: add required check if needed
+ var content = $(tmpl("emptyDiv"));
+ var tabView = new OdinTableView(content, null, null);
+ var createAction = function () {
+ new OdinChooseBox(path, function (selectedItems) {
+ if (isEmpty(selectedItems)) {
+ return;
+ }
+ var currentValues = tabView.getItems();
+ $.merge(currentValues, selectedItems);
+ var existingIDs = [];
+ currentValues = $.grep(currentValues, function (v) {
+ if ($.inArray(v.id, existingIDs) !== -1) {
+ return false;
+ } else {
+ existingIDs.push(v.id);
+ return true;
+ }
+ });
+ tabView.fillBody(metaData, currentValues);
+ });
+ };
+ var deleteAction = function () {
+ var callBack = function () {
+ $.each(tabView.getSelectedItems(), function (index, value) {
+ tabView.deleteById(value.id);
+ });
+ };
+ new OdinConfirmBox(callBack, L10nEnum.QUESTION_DELETE_ITEM);
+ };
+ content.append(tabView);
+ var metaData = [
+ {
+ "fieldType": "string",
+ "fieldName": "id",
+ "caption": "id",
+ "visible": "none"
+ },
+ {
+ "fieldType": "string",
+ "fieldName": "view",
+ "caption": L10nEnum.VIEW,
+ "visible": "all"
+ }
+ ];
+ tabView.createHead(metaData, true);
+ tabView.fillBody(metaData, values);
+ createNavTabCallBack(id, caption, content);
+ var toolbar = new OdinToolbar(content, createAction, null, deleteAction);
+ tabView.setToolbar(toolbar);
+ find(content, CREATE_BUTTON_TEXT_SELECTOR).text(L10nEnum.BUTTON_ADD);
+ return null;
+ };
+
+ var createObjectControl = function (id, caption, value, path) {
+ // TODO: add required check if needed
+ var chooseInput = controlGenerator.createStringInput(
+ id, isEmpty(value) ? "" : value.view, StringTypesEnum.TEXT);
+ chooseInput.addClass(OBJECT_INPUT_CLASS);
+ var buttonGroup = tmpl("buttonGroup");
+ chooseInput.append(buttonGroup);
+ var chooseButton = tmpl("chooseButton");
+ buttonGroup.append(chooseButton);
+ find(buttonGroup, CHOOSE_BTN_SELECTOR).click(function () {
+ new OdinChooseBox(path, function (selectedItems) {
+ if (isEmpty(selectedItems)) {
+ return;
+ }
+ find(chooseInput, CLEARABLE_INPUT_SELECTOR).each(function () {
+ $(this)
+ .val(selectedItems[0].view)
+ .attr(DATA_ATTR, selectedItems[0].id);
+ });
+ });
+ });
+ var clearButton = tmpl("clearButton");
+ buttonGroup.append(clearButton);
+ find(buttonGroup, CLEAR_BTN_SELECTOR).click(function () {
+ find(chooseInput, CLEARABLE_INPUT_SELECTOR).val("");
+ });
+ find(chooseInput, CLEARABLE_INPUT_SELECTOR).each(function () {
+ $(this)
+ .attr(DISABLED_ATTR, true)
+ .attr(DATA_ATTR, isEmpty(value) ? "" : value.id);
+ });
+ var group = controlGenerator.createInputGroup();
+ group.append(controlGenerator.createLabel(id, caption));
+ group.append(chooseInput);
+ return group;
+ };
+
+ var configureControl = function (control, hidden, readonly, notempty) {
+ if (isEmpty(control)) {
+ return;
+ }
+ find(control, LABELS_SELECTOR).each(function () {
+ var currentControl = $(this);
+ if (hidden) {
+ currentControl.hide();
+ }
+ });
+ find(control, INPUTS_SELECTOR).each(function () {
+ var currentControl = $(this);
+ if (hidden) {
+ currentControl.hide();
+ currentControl.attr(TYPE_ATTR, InputTypesEnum.HIDDEN);
+ }
+ if (readonly) {
+ currentControl.attr(DISABLED_ATTR, true);
+ }
+ if (notempty) {
+ currentControl.attr(REQUIRED_ATTR, true);
+ }
+ });
+ return control;
+ };
+
+ this.createFormControl = function (value, fieldParams) {
+ if (isEmpty(fieldParams)) {
+ return null;
+ }
+ var control;
+ switch (fieldParams.fieldType) {
+ case FieldTypesEnum.BOOLEAN:
+ control = createBooleanControl(fieldParams.fieldName,
+ fieldParams.caption, value);
+ break;
+ case FieldTypesEnum.DATE:
+ control = createDateControl(fieldParams.fieldName,
+ fieldParams.caption, value, fieldParams.type);
+ break;
+ case FieldTypesEnum.NUMERIC:
+ control = createNumericControl(fieldParams.fieldName,
+ fieldParams.caption, value, fieldParams.positiveOnly,
+ fieldParams.precision, fieldParams.scale);
+ break;
+ case FieldTypesEnum.STRING:
+ control = createStringControl(fieldParams.fieldName,
+ fieldParams.caption, value, fieldParams.minLength,
+ fieldParams.maxLength, fieldParams.type);
+ break;
+ case FieldTypesEnum.COLLECTION:
+ control = createCollectionControl(fieldParams.fieldName,
+ fieldParams.caption, value, fieldParams.path);
+ break;
+ case FieldTypesEnum.OBJECT:
+ control = createObjectControl(fieldParams.fieldName,
+ fieldParams.caption, value, fieldParams.path);
+ break;
+ default:
+ return null;
+ }
+ var hidden;
+ switch (fieldParams.visible) {
+ case VisibleTypesEnum.NONE:
+ hidden = true;
+ break;
+ case VisibleTypesEnum.ON_CREATE:
+ case VisibleTypesEnum.ON_UPDATE:
+ hidden = isCreateOperation;
+ break;
+ default:
+ hidden = false;
+ }
+ return configureControl(control, hidden, fieldParams.readOnly, fieldParams.notEmpty);
+ };
+
+ this.getCurrentDateStr = function () {
+ return controlGenerator.dateToStr(new Date(), DateTypesEnum.DATE);
+ };
+ }
+
+ function OdinForm(metaData, dataUrl, tableCallBack, id, args) {
+ if (isEmpty(metaData) || isEmpty(dataUrl) || isEmpty(tableCallBack)) {
+ throw "Odin: Not enough parameters";
+ }
+
+ var DEFAULT_TAB_SELECTOR = "odin-values";
+ var TABS_HEADER_SELECTOR = ".nav-tabs";
+ var TABS_CONTENT_SELECTOR = ".tab-content";
+ var TAB_SELECTOR = ".tab-pane";
+ var COLLECTION_ITEM_SELECTOR = "tbody > tr";
+
+ OdinFormCommon.call(this, FormTypesEnum.FORM);
+
+ var self = this;
+
+ var generator;
+
+ var params = isEmpty(args) ? "" : args;
+
+ var isCreateForm = function () {
+ return isEmpty(id);
+ };
+
+ var formToJson = function () {
+ var formData = {};
+ try {
+ findFiltered(self._getContent(), TAB_SELECTOR, "#" + DEFAULT_TAB_SELECTOR)
+ .each(function () {
+ var tabData = [];
+ try {
+ find($(this), COLLECTION_ITEM_SELECTOR)
+ .each(function () {
+ if ($(this).attr(DATA_ATTR)) {
+ tabData.push({
+ id: $(this).attr(DATA_ATTR),
+ view: $(this).attr(DATA_VIEW_ATTR)
+ });
+ return true;
+ }
+ });
+ } catch (e) {
+ // Ignore
+ }
+ formData[$(this).attr(ID_ATTR)] = tabData;
+ });
+ } catch (e) {
+ // Ignore
+ }
+ findFiltered(self._getContent(), INPUTS_SELECTOR, BUTTONS_SELECTOR)
+ .each(function () {
+ if (isEmpty($(this).val())) {
+ return true;
+ }
+ if ($(this).attr(DATA_ATTR)) {
+ formData[$(this).attr(ID_ATTR)] = {
+ id: $(this).attr(DATA_ATTR),
+ view: $(this).val()
+ };
+ return true;
+ }
+ switch ($(this).attr(TYPE_ATTR)) {
+ case InputTypesEnum.CHECKBOX:
+ formData[$(this).attr(ID_ATTR)] = $(this).is(":" + CHECKED_ATTR);
+ break;
+ case InputTypesEnum.DATE:
+ case InputTypesEnum.DATETIME:
+ formData[$(this).attr(ID_ATTR)] = isEmpty($(this).val) ?
+ "" : Date.parse($(this).val());
+ break;
+ case InputTypesEnum.TIME:
+ formData[$(this).attr(ID_ATTR)] = isEmpty($(this).val) ?
+ "" : Date.parse(generator.getCurrentDateStr() +
+ "T" + $(this).val());
+ break;
+ case InputTypesEnum.NUMBER:
+ formData[$(this).attr(ID_ATTR)] = parseFloat($(this).val());
+ break;
+ default:
+ formData[$(this).attr(ID_ATTR)] = $(this).val();
+ }
+ });
+ return JSON.stringify(formData);
+ };
+
+ var initialize = function () {
+ generator = new OdinFormContentGenerator(createNavTab, isCreateForm());
+ self._getContent()
+ .append(tmpl("formTabs", DEFAULT_TAB_SELECTOR, L10nEnum.CAPTION_DEFAULT_TAB));
+ var okBtn = tmpl("okButton", L10nEnum.BUTTON_SAVE);
+ okBtn.attr(TYPE_ATTR, InputTypesEnum.SUBMIT);
+ var closeBtn = tmpl("cancelButton", L10nEnum.BUTTON_CLOSE);
+ self._getFooter().append(closeBtn);
+ self._getFooter().append(okBtn);
+ if (isCreateForm()) {
+ configureAndShowForm();
+ } else {
+ getFromRestWithVersion(dataUrl + "/" + id, function (data) {
+ if (isEmpty(data)) {
+ throw format("Odin: Can't load data by id {0} and url {1}", [id, dataUrl]);
+ }
+ configureAndShowForm(data);
+ });
+ }
+ closeBtn.click(function () {
+ closeForm();
+ });
+ };
+
+ var configureAndShowForm = function (data) {
+ var saveCallBack = function () {
+ saveData(formToJson());
+ };
+ self._setCaption(isEmpty(data) ?
+ L10nEnum.CAPTION_CREATE_FORM :
+ data.view + " " + L10nEnum.CAPTION_EDIT_FORM);
+ self._setFormSubmit(saveCallBack);
+ $.each(metaData, function (fieldIndex, fieldParams) {
+ var value = isEmpty(data) ?
+ null : data[fieldParams.fieldName];
+ find(self._getContent(), "#" + DEFAULT_TAB_SELECTOR)
+ .append(generator.createFormControl(value, fieldParams));
+ });
+ self._show();
+ };
+
+ var createNavTab = function (id, caption, content) {
+ find(self._getContent(), TABS_HEADER_SELECTOR).append(tmpl("formTab", id, caption));
+ var tab = tmpl("formTabContent", id);
+ tab.append(content);
+ find(self._getContent(), TABS_CONTENT_SELECTOR).append(tab);
+ };
+
+ var saveData = function (data) {
+ var saveCallback = function (responseData) {
+ if (isEmpty(responseData)) {
+ throw "Odin: Error on data save";
+ }
+ self._hide();
+ tableCallBack();
+ if (!isEmpty(globalSaveCallback)) {
+ globalSaveCallback();
+ }
+ };
+ if (isCreateForm()) {
+ postToRestWithVersionAndParams(dataUrl, data, params, saveCallback);
+ } else {
+ putToRestWithVersionAndParams(dataUrl, data, params, saveCallback);
+ }
+ };
+
+ var closeForm = function () {
+ if (self._isModified()) {
+ new OdinConfirmBox(self._hide, L10nEnum.QUESTION_LOST_CHANGES);
+ return;
+ }
+ self._hide();
+ };
+
+ initialize();
+ }
+
+ function OdinConfirmBox(callBack, questionText) {
+ if (isEmpty(callBack) || isEmpty(questionText)) {
+ throw "Odin: Not enough parameters";
+ }
+
+ OdinFormCommon.call(this, FormTypesEnum.CONFIRM);
+
+ var self = this;
+
+ var initialize = function () {
+ var yesBtn = tmpl("okButton", L10nEnum.BUTTON_OK);
+ var cancelBtn = tmpl("cancelButton", L10nEnum.BUTTON_CANCEL);
+ self._setCaption(L10nEnum.QUESTION_DEFAULT_CAPTION);
+ self._getContent().text(questionText);
+ self._getFooter().append(yesBtn);
+ self._getFooter().append(cancelBtn);
+ self._show();
+
+ yesBtn.click(function () {
+ self._hide();
+ callBack();
+ if (!isEmpty(globalSaveCallback)) {
+ globalSaveCallback();
+ }
+ });
+ cancelBtn.click(function () {
+ self._hide();
+ });
+ };
+
+ initialize();
+ }
+
+ function OdinChooseBox(path, callBack) {
+ if (isEmpty(path) || isEmpty(callBack)) {
+ throw "Odin: Not enough parameters";
+ }
+
+ var ADDITIONAL_FORM_CLASS = "odin-table-box";
+ var ADDITIONAL_CONTENT_CLASS = "odin-kill-padding";
+ var CONTENT_ID_VALUE = "table-block";
+
+ OdinFormCommon.call(this);
+
+ var self = this;
+
+ var initialize = function () {
+ self._addFormClass(ADDITIONAL_FORM_CLASS);
+ self._getContent().addClass(ADDITIONAL_CONTENT_CLASS);
+ self._getContent().attr(ID_ATTR, CONTENT_ID_VALUE);
+ var yesBtn = tmpl("okButton", L10nEnum.BUTTON_OK);
+ var cancelBtn = tmpl("cancelButton", L10nEnum.BUTTON_CANCEL);
+ self._getFooter().append(cancelBtn);
+ self._getFooter().append(yesBtn);
+ var table = new OdinTableMain("table-block", path + odinMetaList, null, path);
+ self._show();
+
+ yesBtn.click(function () {
+ self._hide();
+ callBack(table.getSelectedItems());
+ });
+ cancelBtn.click(function () {
+ self._hide();
+ });
+ };
+
+ initialize();
+ }
+
+ function OdinCopyBox(dataUrl, tableRedrawCallback) {
+ if (isEmpty(dataUrl) || isEmpty(tableRedrawCallback)) {
+ throw "Odin: Not enough parameters";
+ }
+
+ var ADDITIONAL_FORM_CLASS = "odin-simple-form";
+ var FROM_VERSION_SELECTOR = "#fromVersion";
+ var VERSION_SELECTOR = "#version";
+
+ OdinFormCommon.call(this);
+
+ var self = this;
+
+ var initialize = function () {
+ self._setCaption(L10nEnum.CAPTION_COPY_FORM);
+ self._addFormClass(ADDITIONAL_FORM_CLASS);
+ self._getContent().append(tmpl("copyForm"));
+ var from = find(self._getContent(), FROM_VERSION_SELECTOR);
+ var to = find(self._getContent(), VERSION_SELECTOR);
+ var yesBtn = tmpl("okButton", L10nEnum.BUTTON_OK);
+ var cancelBtn = tmpl("cancelButton", L10nEnum.BUTTON_CANCEL);
+ self._getFooter().append(cancelBtn);
+ self._getFooter().append(yesBtn);
+
+ getFromRest(urlVersions, function (versions) {
+ if (isEmpty(versions) || versions.count === 0) {
+ return;
+ }
+ var versionsData = [];
+ versions.items.forEach(function (version) {
+ versionsData.push({
+ id: version.id,
+ name: version.name + " от " +
+ new Date(version.date).toLocaleDateString(uiLocale)
+ });
+ });
+ fillSelect(from, versionsData.sort(function (a, b) {
+ return a.id - b.id;
+ }));
+ var currentVersion = getCurrentVersionData();
+ if (!isEmpty(currentVersion)) {
+ to.attr(DATA_ATTR, currentVersion.id);
+ to.val(currentVersion.name);
+ }
+ self._show();
+ });
+
+ yesBtn.click(function () {
+ if (isEmpty(from.val()) || isEmpty(to.val())) {
+ return;
+ }
+ if (from.val() === to.attr(DATA_ATTR)) {
+ showFeedbackMessage(L10nEnum.ERROR_VERSIONS_MATCH, MessageTypesEnum.DANGER);
+ return;
+ }
+ postToRestWithVersionAndParams(dataUrl + odinCopy, null,
+ "&fromVersionId=" + from.val(), function () {
+ self._hide();
+ tableRedrawCallback();
+ });
+ });
+ cancelBtn.click(function () {
+ self._hide();
+ });
+ };
+
+ initialize();
+ }
+}
diff --git a/src/main/resources/public/templates/odin.html b/src/main/resources/public/templates/odin.html
new file mode 100644
index 0000000..1f9f9d9
--- /dev/null
+++ b/src/main/resources/public/templates/odin.html
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html
index 9aae873..00e2c0f 100644
--- a/src/main/resources/templates/default.html
+++ b/src/main/resources/templates/default.html
@@ -23,6 +23,7 @@
+
@@ -94,6 +95,10 @@
+
+
+
+
-
-
+
+
+
+
+