globular-mvc
Version:
Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)
1,787 lines (1,455 loc) • 65.9 kB
JavaScript
import '@polymer/iron-icon/iron-icon.js';
import '@polymer/iron-icons/iron-icons.js';
import '@polymer/paper-button/paper-button.js';
import '@polymer/paper-input/paper-input.js';
import '@polymer/paper-listbox/paper-listbox.js';
import '@polymer/paper-item/paper-item.js';
import '@polymer/paper-input/paper-textarea.js';
import '@polymer/paper-dropdown-menu/paper-dropdown-menu.js'
import '@polymer/iron-collapse/iron-collapse.js';
import "@polymer/iron-icons/image-icons";
import * as jwt from "jwt-decode";
import { v4 as uuidv4 } from "uuid";
import { ImageCropper } from "./Image";
import { Camera } from "./Camera";
import { FileExplorer } from "./File"
import { PasswordInput } from "./Password"
import { generatePeerToken, Model } from "../Model";
import { ApplicationView } from "../ApplicationView";
import * as getUuidByString from "uuid-by-string";
import { SetPasswordRequest, SetRootPasswordRequest } from 'globular-web-client/authentication/authentication_pb';
import { Application } from '../Application';
import { Connection, CreateConnectionRqst, DisconnectRqst, StoreType } from 'globular-web-client/persistence/persistence_pb';
/**
* This is the side menu that will be set on left when the user wants to change it settings
* <globular-settings-side-menu>
* <globular-settings-side-menu-item icon="account-box" name="User setting's"> </globular-settings-side-menu-item>
* <globular-settings-side-menu-item icon="application" name="Application setting's"> </globular-settings-side-menu-item>
* </globular-settings-side-menu>
*/
export class SettingsMenu extends HTMLElement {
constructor() {
super();
this.container = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
#container {
display: flex;
flex-direction: column;
}
globular-settings-side-menu-item.active{
color: var(--palette-warning-dark);
}
</style>
<div id="container">
</div>
`;
this.container = this.shadowRoot.getElementById("container")
}
connectedCallback() {
// Set the first item after the Exit menu of course.
this.show()
}
show() {
if (this.container.childNodes.length > 1) {
this.container.childNodes[1].click()
}
}
clear() {
this.container.innerHTML = '';
let item = this.appendSettingsMenuItem("exit-to-app", "Exit")
item.style.order = 1; // That set the item at last position.
}
appendSettingsMenuItem(icon, title) {
const html = `<globular-settings-side-menu-item id="${title}_settings_menu_item" icon="${icon}" title="${title}"> </globular-settings-side-menu-item>`;
const range = document.createRange()
this.container.appendChild(range.createContextualFragment(html))
let item = this.shadowRoot.getElementById(title + "_settings_menu_item")
item.onclick = () => {
let elements = this.shadowRoot.querySelectorAll(".active")
elements.forEach((element) => {
element.classList.remove("active")
})
item.classList.add("active")
Model.eventHub.publish("set-settings-page", title, true)
// close the side menu if it's open...
ApplicationView.layout.appDrawer.close()
}
return item;
}
}
customElements.define("globular-settings-side-menu", SettingsMenu);
/**
* This is the settings side menue item's cotains in the SettingsSideMenu
*/
export class SettingsSideMenuItem extends HTMLElement {
constructor() {
super();
// Set the shadow dom.
this.attachShadow({ mode: "open" });
const icon = this.getAttribute("icon")
const title = this.getAttribute("title")
this.container = null;
this.titleDiv = null;
this.shadowRoot.innerHTML = `
<style>
#container {
display: flex;
position: relative;
align-items: center;
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
margin-right: 10px;
font-weight: 500;
font-size: .75em;
transition: background 0.2s ease,padding 0.8s linear;
background: var(--palette-background-default);
border-radius: 4px;
}
#button{
display: flex;
padding-right: 24px;
}
#container:hover{
cursor: pointer;
-webkit-filter: invert(10%);
filter: invert(10%);
}
</style>
<div id="container">
<div id="button">
<slot></slot>
<iron-icon id="icon" icon="${icon}"></iron-icon>
</div>
<div id="title-div">${title}</div>
<paper-ripple recenters></paper-ripple>
</div>
`;
}
clear() {
this.container.innerHTML = '';
}
// attach event here.
connectedCallback() {
this.container = this.shadowRoot.getElementById("container")
this.titleDiv = this.shadowRoot.getElementById("title-div")
if (this.hasAttribute("icon")) {
if (this.getAttribute("icon").length == 0) {
this.shadowRoot.querySelector("#icon").style.display = "none";
}
}
}
}
customElements.define("globular-settings-side-menu-item", SettingsSideMenuItem);
/**
* This is the settings panel it connect with the side menu to display the correct page.
* It must be set in the workspace when the user select the gear button.
* ex.
* <globular-settings-panel>
* <globular-settings-page>
* <globular-settings title="Apparance">
* <slot>
* <div> <div>
* </slot>
* </globular-settings>
* <globular-settings title="Search engine">
* <slot>
* <div> <div>
* </slot>
* </globular-settings>
* </globular-settings-page>
* </globular-settings-panel>
*/
export class SettingsPanel extends HTMLElement {
constructor() {
super();
this.container = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
// Connect to event.
this.shadowRoot.innerHTML = `
<style>
#container {
display: inline-flex;
flex-direction: column;
margin-top: 25px;
}
</style>
<div id="container">
<slot></slot>
</div>
`;
this.container = this.shadowRoot.getElementById("container")
}
clear() {
let yesNoSetting = new YesNoSetting("", "Do you wish to save your settings?",
() => {
// Save the setting's
Model.eventHub.publish("save_settings_evt", true, true)
},
() => {
// Not save the setting's
Model.eventHub.publish("save_settings_evt", false, true)
})
this.shadowRoot.getElementById("container").innerHTML = "<slot></slot>";
let section = this.appendSettingsPage("Exit").appendSettings("Exit", "Returning to the application...")
section.appendChild(yesNoSetting)
}
appendSettingsPage(title) {
const html = `<globular-settings-page id="${title}_settings_page" title="${title}"></globular-settings-page>`;
const range = document.createRange()
//this.container.appendChild(range.createContextualFragment(html))
this.appendChild(range.createContextualFragment(html))
// const page = this.shadowRoot.querySelector("#" + title + "_settings_page")
const page = this.querySelector("#" + title + "_settings_page")
page.init()
return page
}
}
customElements.define("globular-settings-panel", SettingsPanel);
/**
* This is the settings page
*/
export class SettingsPage extends HTMLElement {
constructor() {
super();
this.container = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
this.title = this.getAttribute("title")
this.subtitle = this.getAttribute("subtitle")
// Connect to event.
this.shadowRoot.innerHTML = `
<style>
#container {
display: none;
flex-direction: column;
}
</style>
<div id="container">
<slot></slot>
</div>
`;
this.container = this.shadowRoot.getElementById("container")
}
getSettings() {
return this.container.childNodes;
}
connectedCallback() {
}
// Model.eventHub must be init.
init() {
Model.eventHub.subscribe("set-settings-page", (uuid) => { }, (pageId) => {
if (this.title == pageId) {
this.container.style.display = "flex"
} else {
this.container.style.display = "none"
}
}, true, this)
}
appendSettings(title, subtitle) {
const id = "_" + uuidv4()// title.split(" ").join("");
const html = `<globular-settings id="${id}" title="${title}" subtitle="${subtitle}"></globular-settings>`;
const range = document.createRange()
this.appendChild(range.createContextualFragment(html))
const settings = this.querySelector("#" + id)
return settings
}
}
customElements.define("globular-settings-page", SettingsPage);
/**
* This is the settings page
*/
export class Settings extends HTMLElement {
constructor() {
super();
this.container = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
this.title = this.getAttribute("title")
this.subtitle = "";
if (this.hasAttribute("subtitle")) {
this.subtitle = this.getAttribute("subtitle")
}
// Connect to event.
this.shadowRoot.innerHTML = `
<style>
#container {
display: flex;
flex-direction: column;
margin-bottom: 10px;
background-color: var(--palette-background-paper);
}
.card-title {
font-size: 1.25rem;
text-transform: uppercase;
font-weight: 400;
letter-spacing: .25px;
outline: none;
position: fixed;
top: -50px;
}
.card-subtitle{
padding: 24px;
letter-spacing: .01428571em;
font-family: Roboto,Arial,sans-serif;
font-size: 1.125rem;
font-weight: 400;
line-height: 1.25rem;
hyphens: auto;
word-break: break-word;
word-wrap: break-word;
color: var(--cr-primary-text-color);
flex-grow: 1;
}
.card-content{
display: flex;
flex-direction: column;
min-width: 728px;
padding: 0px;
font-size: 1rem;
}
@media (max-width: 800px) {
.card-content{
min-width: 580px;
}
}
@media (max-width: 600px) {
.card-content{
min-width: 380px;
}
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
paper-button{
display: flex;
font-size: .85rem;
border: none;
color: var(--palette-text-accent);
background: var(--palette-primary-accent);
max-height: 32px;
}
.complex_setting_panel{
display: none;
}
.complex_setting_panel #back-btn{
display: block;
}
#hide-btn{
align-self: center;
}
#back-btn{
display: none;
}
#add-button {
position: absolute;
top: -40px;
right: 0px;
font-size: 1.25rem;
}
</style>
<div id="container">
<paper-card id="${this.id}">
<h2 class="card-title">${this.title}</h2>
<paper-icon-button id="add-button" icon="icons:add" style="display: none;"></paper-icon-button>
<div style="display: flex; align-items: center;">
<paper-icon-button id="back-btn" icon="arrow-back"></paper-icon-button>
<div class="card-subtitle">${this.subtitle}</div>
<paper-icon-button id="hide-btn" icon="unfold-more"></paper-icon-button>
</div>
<div class="card-content">
<iron-collapse class="card-collapse" opened = "[[opened]]">
<slot id="card-content"></slot>
</iron-collapse>
</div>
</paper-card>
</div>
`;
this.shadowRoot.getElementById("hide-btn").onclick = this.hideSettings.bind(this);
this.container = this.shadowRoot.getElementById("container")
this.backBtn = this.shadowRoot.getElementById("back-btn")
}
showAddButton() {
this.shadowRoot.querySelector("#add-button").style.display = "block"
return this.shadowRoot.querySelector("#add-button");
}
hideAddButton() {
this.shadowRoot.querySelector("#add-button").style.display = "none"
}
hideSettings() {
let button = this.shadowRoot.getElementById("hide-btn")
let content = this.shadowRoot.querySelector(".card-collapse")
if (button && content) {
if (!content.opened) {
button.icon = "unfold-more"
} else {
button.icon = "unfold-less"
}
content.toggle();
}
}
addSetting(setting) {
let e = setting.getElement()
if (e != null) {
e.tabIndex = this.childNodes.length
}
this.appendChild(setting)
}
}
customElements.define("globular-settings", Settings);
/**
* The user general settings.
*/
export class Setting extends HTMLElement {
constructor(name, description, icon) {
super();
this.container = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
if (this.hasAttribute("name")) {
name = this.getAttribute("name")
}
if (!this.hasAttribute("id")) {
// generate a unique id.
this.setAttribute("id", "_" + uuidv4())
}
// set icon to empty icon by default.
if (icon == undefined) {
icon = ""
}
// Connect to event.
this.shadowRoot.innerHTML = `
<style>
.setting-name{
line-height: 1.1rem;
font-size: .85rem;
font-weight: 500;
flex-basis: 156px;
letter-spacing: .07272727em;
text-transform: uppercase;
hyphens: auto;
word-break: break-word;
word-wrap: break-word;
}
.setting-description{
font-size: 1rem;
flex-basis: 328px;
flex-grow: 1;
letter-spacing: .00625em;
font-size: 1rem;
font-weight: 400;
line-height: 1.5rem;
hyphens: auto;
word-break: break-word;
word-wrap: break-word;
width: 100%;
}
#icon-left, icon-right {
display: none;
}
a {
font-size: 1rem;
}
</style>
<iron-icon id="icon-left" icon="${icon}"></iron-icon>
<div id="name-div" class="setting-name">${name}</div>
<div id="description-div" class="setting-description">${description}</div>
<iron-icon id="icon-right" icon=""></iron-icon>
`;
// Set style property of the component itself.
this.style.position = "relative"
this.style.display = "flex"
this.style.color = "var(--cr-primary-text-color)"
this.style.fontFamily = "Roboto,Arial,sans-serif"
this.style.alignItems = "center"
this.style.padding = "15px 16px 16px 16px"
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
let w = entry.contentRect.width
this.style.flexDirection = "row";
this.style.alignItems = "center"
this.shadowRoot.getElementById("name-div").style.flexBasis = "156px";
this.shadowRoot.getElementById("description-div").style.flexBasis = "328px";
let textArea = this.shadowRoot.querySelector("paper-textarea")
if (textArea != undefined) {
textArea.style.width = "0px"
}
let paperInput = this.shadowRoot.querySelector("paper-input")
if (paperInput != undefined) {
paperInput.style.width = ""
}
let nextPageBtn = this.shadowRoot.querySelector("#icon-right")
nextPageBtn.style.position = "";
nextPageBtn.style.right = "0px";
if (w < 780) {
this.shadowRoot.getElementById("name-div").style.flexBasis = "100px";
this.shadowRoot.getElementById("description-div").style.flexBasis = "200px";
if (w < 600) {
nextPageBtn.style.position = "absolute";
this.style.flexDirection = "column";
nextPageBtn.style.top = "45%";
this.shadowRoot.getElementById("name-div").style.flexBasis = "0px";
this.shadowRoot.getElementById("description-div").style.flexBasis = "0px";
this.style.alignItems = "flex-start"
let textArea = this.shadowRoot.querySelector("paper-textarea")
if (textArea != undefined) {
textArea.style.width = "335px"
}
if (paperInput != undefined) {
paperInput.style.width = "100%"
}
}
}
}
});
resizeObserver.observe(document.body);
// The name (label) div
this.name = this.shadowRoot.getElementById("name-div")
// The description...
this.description = this.shadowRoot.getElementById("description-div")
}
connectedCallback() {
}
getElement() { return null; }
getValue() { return null; }
getName() { return this.name.innerText; }
getNameDiv() { return this.name; }
getDescription() { return this.description.innerText; }
getDescriptionDiv() { return this.description; }
setDescription(value) {
this.description.innerText = value
}
}
customElements.define("globular-setting", Setting);
export class ConnectionsSetting extends HTMLElement {
constructor(connections) {
super();
// That function is call when a new connection button is click.
this.onCreateConnection = null
// Set the shadow dom.
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
#container {
display: flex;
flex-direction: column;
}
globular-settings-side-menu-item.active{
color: var(--palette-warning-dark);
}
</style>
<div id="container">
<slot></slot>
</div>
`;
for (var name in connections) {
this.addConnectionSetting(name, connections[name])
}
}
// call when the element is insert in it parent.
connectedCallback() {
let addButton = this.parentNode.showAddButton()
addButton.onclick = () => {
if (this.onCreateConnection != null) {
this.onCreateConnection()
}
}
}
addConnectionSetting(name, connection) {
let connections = this.querySelectorAll("globular-connection-setting")
for (var i = 0; i < connections.length; i++) {
connections[i].hide()
}
let connectionSetting = new ConnectionSetting(name, connection)
this.appendChild(connectionSetting)
connectionSetting.show()
}
getElement() {
return this.shadowRoot.querySelector("#container")
}
}
customElements.define("globular-connections-setting", ConnectionsSetting);
/**
* Generic connection setting...
*/
export class ConnectionSetting extends HTMLElement {
constructor(name, connection) {
super();
// Set the connection.
this.onDeleteConnection = null;
// Set the shadow dom.
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<style>
#container{
display: flex;
flex-direction: column;
align-items: center;
border-bottom: 1px solid var(--palette-background-default);
}
#content{
padding: 15px;
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
.header{
display: flex;
align-items: center;
width: 100%;
transition: background 0.2s ease,padding 0.8s linear;
background-color: var(--palette-background-paper);
}
.header paper-icon-button {
min-width: 40px;
}
.header:hover{
-webkit-filter: invert(10%);
filter: invert(10%);
}
.title{
flex-grow: 1;
margin: 8px;
font-size: 1.1rem;
}
img, iron-icon{
margin: 8px;
}
#collapse-panel{
display: flex;
flex-direction: column;
width: 100%;
}
#delete-btn {
font-size: .85rem;
max-height: 32px;
}
.table{
display: table;
}
.row{
display: table-row;
}
.cell{
display: table-cell;
vertical-align: middle;
}
#content {
display: flex;
flex-direction: column;
margin: 10px;
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<div id="container">
<div class="header">
<span class="title">${name}</span>
<paper-button id="delete-btn" icon="delete">DELETE</paper-button>
<div style="display: flex; width: 32px; height: 32px; justify-content: center; align-items: center;position: relative;">
<iron-icon id="hide-btn" icon="unfold-less" style="flex-grow: 1; --iron-icon-fill-color:var(--palette-text-primary);" icon="add"></iron-icon>
<paper-ripple class="circle" recenters=""></paper-ripple>
</div>
</div>
<iron-collapse id="collapse-panel" >
<div id="content" class="table">
</div>
</iron-collapse>
</div>
`;
let togglePanel = this.shadowRoot.querySelector("#collapse-panel")
this.hideBtn = this.shadowRoot.querySelector("#hide-btn")
let deleteBtn = this.shadowRoot.querySelector("#delete-btn")
deleteBtn.onclick = () => {
let toast = ApplicationView.displayMessage(
`
<style>
#yes-no-contact-delete-box{
display: flex;
flex-direction: column;
}
#yes-no-contact-delete-box globular-contact-card{
padding-bottom: 10px;
}
#yes-no-contact-delete-box div{
display: flex;
padding-bottom: 10px;
}
</style>
<div id="yes-no-connection-delete-box">
<div>Your about to delete connection <span style="font-style: italic;">${connection.Id}</span></div>
<div>Is it what you want to do? </div>
<div style="justify-content: flex-end;">
<paper-button id="yes-delete-connection">Yes</paper-button>
<paper-button id="no-delete-connection">No</paper-button>
</div>
</div>
`,
15000 // 15 sec...
);
let yesBtn = document.querySelector("#yes-delete-connection")
let noBtn = document.querySelector("#no-delete-connection")
// On yes
yesBtn.onclick = () => {
this.parentNode.removeChild(this)
if (this.onDeleteConnection) {
this.onDeleteConnection()
}
}
noBtn.onclick = () => {
toast.dismiss();
}
}
// give the focus to the input.
this.hideBtn.onclick = () => {
let button = this.shadowRoot.querySelector("#hide-btn")
if (button && togglePanel) {
if (!togglePanel.opened) {
button.icon = "unfold-more"
} else {
button.icon = "unfold-less"
}
togglePanel.toggle();
}
}
this.appendConnectionBody(connection)
}
show() {
let togglePanel = this.shadowRoot.querySelector("#collapse-panel")
let button = this.shadowRoot.querySelector("#hide-btn")
if (button && togglePanel) {
if (!togglePanel.opened) {
button.icon = "unfold-more"
togglePanel.toggle();
}
}
}
hide() {
let togglePanel = this.shadowRoot.querySelector("#collapse-panel")
let button = this.shadowRoot.querySelector("#hide-btn")
if (button && togglePanel) {
if (togglePanel.opened) {
button.icon = "unfold-less"
togglePanel.toggle();
}
}
}
appendConnectionBody(connection) {
let html = `
<div class="row">
<div class="cell">id</div>
<paper-input id="id-input" class="cell"></paper-input>
</div>
<div class="row">
<div class="cell">host</div>
<paper-input id="host-input" class="cell"></paper-input>
</div>
<div class="row">
<div class="cell">user</div>
<paper-input id="user-input" class="cell"></paper-input>
</div>
<div class="row">
<div class="cell">password</div>
<paper-input id="password-input" type="password" class="cell"></paper-input>
</div>
<div class="row">
<div class="cell">port</div>
<paper-input id="port-input" type="number" min="1" step="1" class="cell"></paper-input>
</div>
`
let content = this.shadowRoot.querySelector("#content")
let range = document.createRange()
content.appendChild(range.createContextualFragment(html))
// Set the values.
this.id_input = content.querySelector("#id-input");
this.id_input.value = connection.Id
this.id_input.onchange = () => {
connection.Id = this.id_input.value
if (this.onchange) {
this.onchange()
}
}
this.id_input.onkeyup = () => {
this.shadowRoot.querySelector(".title").innerHTML = this.id_input.value
}
this.host_input = content.querySelector("#host-input");
this.host_input.value = connection.Host
this.host_input.onchange = () => {
connection.Host = this.host_input.value
if (this.onchange) {
this.onchange()
}
}
this.user_input = content.querySelector("#user-input");
this.user_input.value = connection.User
this.user_input.onchange = () => {
connection.User = this.user_input.value
if (this.onchange) {
this.onchange()
}
}
this.password_input = content.querySelector("#password-input");
this.password_input.value = connection.Password
this.password_input.onchange = () => {
connection.Password = this.password_input.value
if (this.onchange) {
this.onchange()
}
}
this.port_input = content.querySelector("#port-input");
this.port_input.value = connection.Port
this.port_input.onchange = () => {
connection.Port = this.port_input.value
if (this.onchange) {
this.onchange()
}
}
}
}
customElements.define("globular-connection-setting", ConnectionSetting);
/**
* That class must be use for setting that need more informations.
*/
export class ComplexSetting extends Setting {
constructor(name, description, icon) {
super(name, description);
let range = document.createRange()
let html = `
<style>
#icon-right:hover{
cursor: pointer;
}
`
this.shadowRoot.appendChild(range.createContextualFragment(html))
this.actionBtn = this.shadowRoot.getElementById("icon-right")
this.actionBtn.icon = "chevron-right"
this.actionBtn.style.display = "block";
this._parentPage = null;
this._parentPage = null;
this._container = null;
this._panel = null;
this._settings = {};
this.actionBtn.onclick = () => {
for (var i = 0; i < this._parentPage.children.length; i++) {
let node = this._parentPage.children[i];
node.style.display = "none"
}
// display the settings.
this._panel.style.display = "block"
if (this._panel.children.length > 0) {
let e = this._panel.children[0].getElement()
if (e != undefined) {
e.focus()
}
}
}
}
getElement() {
return this._panel;
}
// add settings...
addSetting(setting) {
this._settings[setting.id] = setting;
}
// Get the parent page of the complex settings.
getParentPage(node) {
if (node.parentNode != undefined) {
if (node.parentNode.tagName == "GLOBULAR-SETTINGS-PAGE") {
return node.parentNode
}
return this.getParentPage(node.parentNode);
}
return null;
}
connectedCallback() {
// did it onces...
if (this._parentPage == null) {
this._parentPage = this.getParentPage(this);
this._panel = this._parentPage.appendSettings(this.getName(), this.getDescription())
this._panel.style.display = "none"
this._panel.backBtn.style.display = "block"
this._panel.classList.add("complex_setting_panel")
// hide the panel and display back the content of the page.
this._panel.backBtn.onclick = () => {
for (var i = 0; i < this._parentPage.children.length; i++) {
let node = this._parentPage.children[i];
if (!node.classList.contains("complex_setting_panel")) {
node.style.display = ""
}
}
// display the settings.
this._panel.style.display = "none"
}
// add the settings.
for (var id in this._settings) {
this._panel.addSetting(this._settings[id])
}
}
}
getSetting(id) { return this._settings[id] }
}
customElements.define("globular-complex-setting", ComplexSetting);
/**
* Set string setting...
*/
export class ReadOnlyStringSetting extends Setting {
constructor(name, description) {
super(name, description);
this.onchange = null;
let html = `
<style>
#setting-span{
flex-grow: 1;
font-size: 1rem;
}
</style>
<span id="setting-span" label=""></span>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.span = this.shadowRoot.getElementById("setting-span");
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.span.label = description;
}
this.span.setAttribute("title", description);
this.span.onblur = () => {
if (this.onblur != null) {
this.onblur()
}
}
}
getElement() {
return this.span;
}
getValue() {
return this.span.innerHTML
}
setValue(value) {
this.span.innerHTML = value;
}
}
customElements.define("globular-read-only-string-setting", ReadOnlyStringSetting);
/**
* Set string setting...
*/
export class LinkSetting extends Setting {
constructor(name, description) {
super(name, description);
this.onchange = null;
let html = `
<style>
#setting-span{
flex-grow: 1;
font-size: 1rem;
color: var(--cr-primary-text-color);
}
</style>
<a style="color: var(--cr-primary-text-color);" target="_blank" rel="noopener noreferrer" id="setting-url" label=""></a>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.link = this.shadowRoot.getElementById("setting-url");
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.link.label = description;
}
this.link.setAttribute("title", description);
}
getElement() {
return this.link;
}
getValue() {
return this.link.innerHTML
}
setValue(value) {
this.link.innerHTML = value;
}
setUrl(url) {
this.link.setAttribute("href", url)
}
}
customElements.define("globular-link-setting", LinkSetting);
/**
* Set string setting...
*/
export class StringSetting extends Setting {
constructor(name, description) {
super(name, description);
this.onchange = null;
let html = `
<style>
#setting-input{
flex-grow: 1;
font-size: 1rem;
}
</style>
<paper-input id="setting-input" label="" raised></paper-input>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.input = this.shadowRoot.getElementById("setting-input");
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.input.label = description;
}
this.input.setAttribute("title", description);
this.input.onblur = () => {
if (this.onblur != null) {
this.onblur()
}
}
}
getElement() {
return this.input;
}
getValue() {
return this.input.value
}
setValue(value) {
this.input.value = value;
if (this.onchange != null) {
this.onchange(value);
}
}
}
customElements.define("globular-string-setting", StringSetting);
/**
* Set exclusive select setting...
*/
export class RadioGroupSetting extends Setting {
constructor(name, description) {
super(name, description);
this.onchange = null;
let html = `
<style>
paper-radio-button[checked]{
--paper-radio-button-label-color: var(--palette-text-accent);
}
</style>
<paper-radio-group selected=""></paper-radio-group>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.radioBtnGrp = this.shadowRoot.querySelector("paper-radio-group");
this.description.style.display = "none";
}
getElement() {
return this.radioBtnGrp;
}
getValue() {
return this.input.value
}
setValue(value) {
this.radioBtnGrp.setAttribute("selected", value)
this.shadowRoot.querySelector(`#${value}-radio-btn`).click()
}
// That function must be overide.
onSelect(value) {
}
setChoices(values) {
values.forEach(val => {
let choice = document.createElement("paper-radio-button")
choice.name = val
choice.id = val + "-radio-btn"
choice.innerHTML = val
this.radioBtnGrp.appendChild(choice)
choice.onclick = () => {
if (this.onSelect != null) {
this.onSelect(choice.name)
}
}
})
}
}
customElements.define("globular-radio-group-setting", RadioGroupSetting);
/**
* Set string setting...
*/
export class TextAreaSetting extends Setting {
constructor(name, description) {
super(name, description);
let html = `
<style>
#setting-input{
flex-grow: 1;
font-size: 1rem;
}
</style>
<paper-textarea id="setting-input" style="width: 0px;" label="" raised></paper-textarea>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.input = this.shadowRoot.getElementById("setting-input");
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.input.label = description;
}
this.input.setAttribute("title", description);
}
getElement() {
return this.input;
}
getValue() {
return this.input.value
}
setValue(value) {
this.input.value = value;
}
}
customElements.define("globular-textarea-setting", TextAreaSetting);
/**
* true false on of...
*/
export class OnOffSetting extends Setting {
constructor(name, description) {
super(name, description);
let html = `
<style>
#setting-input{
flex-grow: 1;
font-size: 1rem;
}
paper-toggle-button[checked]{
--paper-toggle-button-label-color: var(--palette-text-accent);
}
</style>
<paper-toggle-button id="setting-input">${description}</paper-toggle-button>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.input = this.shadowRoot.getElementById("setting-input");
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.input.label = description;
}
this.input.setAttribute("title", description);
this.getDescriptionDiv().style.display = "none";
}
getElement() {
return this.input;
}
getValue() {
return this.input.checked
}
setValue(value) {
this.input.checked = value;
}
}
customElements.define("globular-on-off-setting", OnOffSetting);
/**
* Add email validation to the string setting.
*/
export class NumberSetting extends StringSetting {
constructor(name, description) {
super(name, description)
// email need's validation so here's I will set it.
this.getElement().type = "number"
}
}
customElements.define("globular-number-setting", NumberSetting);
/**
* Set image setting...
*/
export class ImageSetting extends Setting {
constructor(name, description) {
super(name, description);
this.onchange = null;
let html = `
<style>
#custom-file-upload{
display: flex;
flex-grow: 1;
}
#custom-file-upload span{
flex-grow: 1;
}
#custom-file-upload iron-icon{
padding-right: 15px;
}
#custom-file-upload div{
display: flex;
align-items: center;
border-bottom: 1px solid var(--palette-text-primary);
font-size: 1rem;
flex-basis: 100%;
letter-spacing: .00625em;
font-weight: 400;
line-height: 1.5rem;
word-break: break-word;
margin-right: 10px;
margin-top: 10px;
}
#custom-file-upload div:hover{
cursor: pointer;
border-bottom: 2px solid var(--palette-primary-accent);
}
input[type="file"] {
display: none;
}
img {
padding: 5px;
max-width: 128px;
max-height: 128px;
}
#image-display-div{
display: flex;
align-items: center;
justify-content: center;
min-width: 64px;
min-height: 64px;
}
#no-image-display{
--iron-icon-height: 48px;
--iron-icon-width: 48px;
--iron-icon-fill-color: lightgray;
border: 1px solid lightgray;
border-radius: 3px;
}
#image-display{
display: none;
border: 1px solid lightgray;
border-radius: 3px;
}
#container{
flex-grow: 1;
display: flex;
align-items: baseline;
}
@media (max-width: 800px) {
#container{
flex-direction: column-reverse;
}
}
</style>
<div id="container">
<div id="custom-file-upload">
<div>
<iron-icon icon="cloud-upload"> </iron-icon>
<span>${description}</span>
</div>
</div>
<div id="image-display-div">
<img id="image-display" src="#" />
<iron-icon id="no-image-display" icon="image:photo"></iron-icon>
</div>
</div>
<input type="file" id="setting-input"></input>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
// Init the interface component here.
this.image = this.shadowRoot.getElementById("image-display");
this.icon = this.shadowRoot.getElementById("no-image-display");
this.input = this.shadowRoot.getElementById("setting-input");
this.input.onchange = (evt) => {
let files = evt.target.files;
if (files && files[0]) {
let reader = new FileReader()
reader.onload = (e) => {
this.image.src = e.target.result
// Set the change event.
if (this.onchange != null) {
this.onchange(this.image.src) // event...
}
this.image.style.display = "block";
this.icon.style.display = "none";
}
reader.readAsDataURL(files[0])
}
}
this.shadowRoot.getElementById("custom-file-upload").onclick = () => {
this.input.click();
}
this.description.style.display = "none";
this.setAttribute("title", "")
if (description.length > 0) {
this.input.label = description;
}
this.input.setAttribute("title", description);
}
getValue() {
return this.image.src;
}
/**
* This must be a blob or something acceptable by an img tag
* @param {*} value
*/
setValue(value) {
this.image.src = value
this.image.style.display = "block";
this.icon.style.display = "none";
}
}
customElements.define("globular-image-setting", ImageSetting);
export class PasswordSetting extends Setting {
constructor(name, description) {
super(name, description);
this.itemsArray = []
let html = `
<style>
#setting-input{
flex-grow: 1;
font-size: 1rem;
}
#content{
display: flex;
flex-direction: column;
width: 100%;
padding: 0px 20px;
}
globular-password-input{
margin-bottom: 20px;
}
</style>
<div id="content">
<globular-password-input id="actual-password" label="actual password"></globular-password-input>
<globular-password-input id="new-password" label="new password"></globular-password-input>
<globular-password-input id="confirm-new-password" label="confirm new password"></globular-password-input>
<div style="display: flex;">
<span style="flex-grow: 1;"></span>
<paper-button id="submit-btn" disabled>Submit</paper-button>
</div>
</div>
`
let range = document.createRange();
let content = range.createContextualFragment(html)
this.shadowRoot.insertBefore(content, this.description)
this.actualPasswordInput = this.shadowRoot.getElementById("actual-password");
this.newPasswordInput = this.shadowRoot.getElementById("new-password");
this.confirmNewPassword = this.shadowRoot.getElementById("confirm-new-password");
this.submitBtn = this.shadowRoot.querySelector("#submit-btn")
// now show the actual description.
this.description.style.display = "none";
// test if the password match...
this.newPasswordInput.onkeyup = this.confirmNewPassword.onkeyup = () => {
// disabled the submit button
this.submitBtn.setAttribute("disabled", "")
if (this.actualPasswordInput.getPassword().length > 0 &&
this.newPasswordInput.getPassword().length > 0 &&
this.confirmNewPassword.getPassword().length > 0 && this.confirmNewPassword.getPassword() == this.newPasswordInput.getPassword()) {
this.submitBtn.removeAttribute("disabled")
}
}
this.submitBtn.onclick = () => {
let globule = Model.getGlobule(Application.account.domain)
generatePeerToken(globule, token => {
let setUserConnection = (token, callback, errorCallback) => {
let decoded = jwt(token);
let id = decoded.id;
let userName = decoded.username;
let email = decoded.email;
let domain = decoded.user_domain;
// here I will save the user token and user_name in the local storage.
localStorage.setItem("user_token", token);
localStorage.setItem("token_expired", decoded.exp);
localStorage.setItem("user_id", id);
localStorage.setItem("user_name", userName);
localStorage.setItem("user_email", email);
localStorage.setItem("user_domain", domain);
let connectionId = userName.split("@").join("_").split(".").join("_");
let rqst = new DisconnectRqst
rqst.setConnectionid(connectionId)
globule.persistenceService.disconnect(rqst, { token: token, application: Model.application, domain: globule.domain })
.then(rsp => {
// Create connection.
let rqst = new CreateConnectionRqst
// So here i will open the use database connection.
let connection = new Connection
connection.setId(connectionId)
connection.setUser(connectionId)
connection.setPassword(this.newPasswordInput.getPassword())
connection.setStore(StoreType.MONGO)
connection.setName(id)
connection.setPort(globule.config.BackendPort)
connection.setTimeout(60)
connection.setHost(domain)
rqst.setConnection(connection)
globule.persistenceService.createConnection(rqst, {
token: token,
application: Model.application,
domain: globule.domain
}).then(callback)
.catch(errorCallback(err))
})
}
// If the user is the root I will also change the root password.
if (Application.account.id == "sa") {
let rqst = new SetRootPasswordRequest
rqst.setNewpassword(this.newPasswordInput.getPassword())
rqst.setOldpassword(this.actualPasswordInput.getPassword())
globule.authenticationService.setRootPassword(rqst, { token: token, application: Model.application, domain: globule.domain })
.then(rsp => setUserConnection(rsp.getToken(), () => {
ApplicationView.displayMessage("Password was updated!", 3000)
ApplicationView.resume()
}, err => { ApplicationView.displayMessage(err, 3000); ApplicationView.resume() }))
.catch(err => { ApplicationView.displayMessage(err, 3000); ApplicationView.resume() })
} else {
let rqst = new SetPasswordRequest
rqst.setOldpassword(this.actualPasswordInput.getPassword())
rqst.setNewpassword(this.newPasswordInput.getPassword())
rqst.setAccountid(Application.account.id + "@" + Application.account.domain)
globule.authenticationService.setPassword(rqst, { token: token, application: Model.application, domain: globule.domain })
.then(rsp =>setUserConnection(rsp.getToken(), () => {
ApplicationView.displayMessage("Password was updated!", 3000)
ApplicationView.resume()
}, err => { ApplicationView.displayMessage(err, 3000); ApplicationView.resume() })
).catch(err => { ApplicationView.displayMessage(err, 3000); ApplicationView.resume() })
}
})
}
}
getElement() {
return this.shadowRoot.querySelector("#content");
}
getValue() {
return ""
}
setValue(value) {
this.input.value = value;
}
}
customElements.define("globular-password-setting", PasswordSetting);
/**
* Set string setting...
*/
export class ImageCropperSetting extends Setting {
constructor(name, description, accountId, dataUrl) {
super(name, description);
let html = `
<style>
#setting-input{
font-size: 1rem;
}
</style>
<div style='width:100%;min-height:450px;position:relative;background-color:var(--palette-background-default);'>
<globular-image-cropper id='mycrop' width='200px' height='200px'>
<div slot='selectText'>Select image</div>
<div slot='cropText'>Crop image</div>
<div slot='resetText'>Reset</div>
</globular-image-cropper>
<globular-camera id="polaroid" width="616"></globular-camera>
</div>
`
let range = document.createRange();
this.title = description;
this.shadowRoot.insertBefore(range.createContextualFragment(html), this.description)
this.description.style.display = "none"
this.name.style.display = "none"
this.shadowRoo