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,318 lines (1,104 loc) • 231 kB
JavaScript
import { generatePeerToken, Model } from '../Model';
import { Application } from "../Application";
import { CreateVideoRequest, DeleteTitleRequest, DeleteVideoRequest, DissociateFileWithTitleRequest, GetTitleFilesRequest, Poster, Person, Publisher, SearchTitlesRequest, SearchPersonsRequest, DeletePersonRequest, GetPersonByIdRequest, CreatePersonRequest, CreateTitleRequest, CreateAudioRequest, UpdateTitleMetadataRequest, UpdateVideoMetadataRequest } from "globular-web-client/title/title_pb";
import { File } from "../File";
import { VideoPreview, getFileSizeString } from "./File";
import { ApplicationView } from "../ApplicationView";
import { formatBoolean, randomUUID } from "./utility";
import { playVideo } from "./Video";
import { playAudio, secondsToTime } from "./Audio";
import '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
import { EditableStringList } from "./List";
import { createThumbmail, readBlogPost } from './BlogPost';
import { PermissionsManager } from './Permissions';
import * as getUuidByString from 'uuid-by-string';
import { SavePackageResponse } from 'globular-web-client/catalog/catalog_pb';
import { GetFileMetadataRequest } from 'globular-web-client/file/file_pb';
// extract the duration info from the raw data.
function parseDuration(duration) {
// display the track lenght...
let obj = secondsToTime(duration)
var hours = obj.h
var min = obj.m
var sec = obj.s
let hours_ = (hours < 10) ? '0' + hours : hours;
let minutes_ = (min < 10) ? '0' + min : min;
let seconds_ = (sec < 10) ? '0' + sec : sec;
if (hours > 0)
return hours_ + ":" + minutes_ + ":" + seconds_;
if (min > 0)
return minutes_ + ":" + seconds_;
return seconds_ + "'s";
}
function listToString(lst) {
let str = "["
if (lst) {
lst.forEach((s, i) => {
str += s;
if (i < lst.length - 1) {
str += " ,"
}
})
}
str += "]"
return str
}
const __style__ = `
.title-div {
display: flex;
}
.title-poster-div {
padding-right: 20px;
}
.title-informations-div {
font-size: 1em;
min-width: 350px;
max-width: 450px;
}
.title-poster-div img, p{
/*max-width: 256px;*/
}
.title-genre-span {
border: 1px solid var(--palette-divider);
padding: 1px;
padding-left: 5px;
padding-right: 5px;
margin-right: 5px;
user-select: none;
}
.rating-star{
--iron-icon-fill-color: rgb(245 197 24);
padding-right: 10px;
height: 30px;
width: 30px;
}
.title-rating-div{
display: flex;
align-items: center;
color: var(--palette-text-secondery);
font-size: 1rem;
}
.title-genres-div{
padding: 5px;
display: flex;
flex-wrap: wrap;
font-size: .9rem;
}
#rating-span{
font-weight: 600;
font-size: 1.2rem;
color: var(--palette-text-primery);
user-select: none;
}
.title-credit {
flex-grow: 1;
color: var(--palette-text-primery);
border-bottom: 2px solid;
border-color: var(--palette-divider);
width: 100%;
margin-bottom: 10px;
}
.title-files-div {
display: flex;
width: 100%;
flex-wrap: wrap;
max-width: 400px;
}
.title-files-div globular-video-preview {
margin-right: 5px;
}
.title-files-div paper-progress{
width: 100%;
}
.title-top-credit, title-credit{
margin-top: 15px;
display: flex;
flex-direction: column;
}
.title-credit-title{
font-weight: 400;
font-size: 1.1rem;
color: var(--palette-text-primery);
}
.title-credit-lst{
display: flex;
}
.title-credit-lst a {
color: var(--palette-warning-main);
font-size: 1.1rem;
margin-right: 12px;
}
.title-credit-lst a:link { text-decoration: none; }
.title-credit-lst a:visited { text-decoration: none; }
.title-credit-lst a:hover { text-decoration: none; cursor:pointer; }
.title-credit-lst a:active { text-decoration: none; }
/** Small **/
@media only screen and (max-width: 600px) {
.title-div {
flex-direction: column;
}
.title-poster-div {
display: flex;
justify-content: center;
margin-bottom: 20px;
flex-direction: column;
}
globular-video-preview{
margin-top: 10px;
}
}
`
// Create the video preview...
function getVideoPreview(parent, path, name, callback, globule) {
// set only one preview div...
let id = "_" + getUuidByString(path) + "_preview_div"
if (parent.querySelector("#" + id) != null) {
callback(parent.querySelector("#" + id))
return
}
let h = 64;
let w = 100;
File.getFile(globule, path, w, h, file => {
let fileNameSpan = document.createElement("span")
let preview = new VideoPreview(file, 64, () => {
if (preview.width > 0 && preview.height > 0) {
w = (preview.width / preview.height) * h
}
fileNameSpan.style.wordBreak = "break-all"
fileNameSpan.style.fontSize = ".85rem"
fileNameSpan.style.maxWidth = w + "px";
fileNameSpan.innerHTML = path.substring(path.lastIndexOf("/") + 1)
}, globule)
let range = document.createRange()
let uuid = randomUUID()
preview.appendChild(range.createContextualFragment(`<paper-icon-button icon="icons:remove-circle" id="_${uuid}" style="position: absolute;"> </paper-icon-button>`))
let unlinkBtn = preview.querySelector(`#_${uuid}`)
// keep the explorer link...
preview.name = name
preview.onpreview = () => {
let previews = parent.querySelectorAll("globular-video-preview")
previews.forEach(p => {
// stop all other preview...
if (preview.name != p.name) {
p.stopPreview()
}
})
}
// Here I will set the filename
let previewDiv = document.createElement("div")
previewDiv.id = id
previewDiv.style.display = "flex"
previewDiv.style.flexDirection = "column"
previewDiv.appendChild(preview)
previewDiv.appendChild(fileNameSpan)
// When the file will be unlink...
unlinkBtn.onclick = (evt) => {
evt.stopPropagation();
let toast = ApplicationView.displayMessage(`
<style>
</style>
<div id="select-media-dialog">
<div>Your about to delete file association</div>
<p style="font-style: italic; max-width: 300px;" id="file-name"></p>
<div>Is that what you want to do? </div>
<div style="display: flex; justify-content: flex-end;">
<paper-button id="imdb-lnk-ok-button">Ok</paper-button>
<paper-button id="imdb-lnk-cancel-button">Cancel</paper-button>
</div>
</div>
`, 60 * 1000)
let cancelBtn = toast.el.querySelector("#imdb-lnk-cancel-button")
cancelBtn.onclick = () => {
toast.dismiss();
}
toast.el.querySelector("#file-name").innerHTML = path.substring(path.lastIndexOf("/") + 1)
let okBtn = toast.el.querySelector("#imdb-lnk-ok-button")
okBtn.onclick = () => {
let rqst = new DissociateFileWithTitleRequest
rqst.setFilepath(path)
rqst.setTitleid(name)
if (name.startsWith("tt")) {
rqst.setIndexpath(globule.config.DataPath + "/search/titles")
} else {
rqst.setIndexpath(globule.config.DataPath + "/search/videos")
}
globule.titleService.dissociateFileWithTitle(rqst, { application: Application.application, domain: Application.domain, token: localStorage.getItem("user_token") })
.then(rsp => {
// remove the association.
previewDiv.parentNode.removeChild(previewDiv)
ApplicationView.displayMessage("association was delete", 3000)
toast.dismiss();
}).catch(err => ApplicationView.displayMessage(err, 3000))
}
}
preview.onplay = (f) => {
path = f.path
if (path.endsWith(".mp3")) {
playAudio(path, (audio) => {
let titleInfoBox = document.getElementById("title-info-box")
if (titleInfoBox) {
titleInfoBox.parentNode.removeChild(titleInfoBox)
}
//video.toggleFullscreen();
}, null, null, globule)
} else {
playVideo(path, (video) => {
let titleInfoBox = document.getElementById("title-info-box")
if (titleInfoBox) {
titleInfoBox.parentNode.removeChild(titleInfoBox)
}
if (video.toggleFullscreen) {
video.toggleFullscreen();
}
}, null, null, globule)
}
}
callback(previewDiv)
}, err => ApplicationView.displayMessage(err, 3000))
}
function GetTitleFiles(indexPath, title, parent, callback) {
let previews = []
let titleFiles = () => {
let globule = title.globule
__getTitleFiles__(globule, globule.config.DataPath + indexPath, title, parent, previews_ => {
previews = previews.concat(previews_)
callback(previews)
})
}
titleFiles() // call once
}
/**
* Return the list of file of a given tile...
* @param {*} title The title
* @param {*} callback
*/
function __getTitleFiles__(globule, indexPath, title, parent, callback) {
let rqst = new GetTitleFilesRequest
rqst.setTitleid(title.getId())
rqst.setIndexpath(indexPath)
globule.titleService.getTitleFiles(rqst, { application: Application.application, domain: Application.domain, token: localStorage.getItem("user_token") })
.then(rsp => {
let previews = []
let _getVideoPreview_ = () => {
if (rsp.getFilepathsList().length > 0) {
let path = rsp.getFilepathsList().pop()
// Return the first file only...
getVideoPreview(parent, path, title.getId(), p => {
parent.appendChild(p)
_getVideoPreview_() // call again...
}, globule)
} else {
callback(previews)
}
}
_getVideoPreview_() // call once...
})
.catch(err => { callback([]) })
}
export function searchEpisodes(serie, indexPath, callback) {
let episodes = []
let globules = Model.getGlobules()
let __searchEpisodes__ = (globules) => {
let globule = globules.pop()
// search episodes...
_searchEpisodes_(globule, serie, indexPath, episodes_ => {
episodes = episodes.concat(episodes_)
if (globules.length > 0) {
__searchEpisodes__(globules)
} else {
callback(episodes)
}
})
}
if (globules.length > 0) {
__searchEpisodes__(globules)
}
}
function _searchEpisodes_(globule, serie, indexPath, callback) {
// This is a simple test...
let rqst = new SearchTitlesRequest
rqst.setIndexpath(indexPath)
rqst.setQuery(serie)
rqst.setOffset(0)
rqst.setSize(1000)
let episodes = []
let stream = globule.titleService.searchTitles(rqst, { application: Application.application, domain: Application.domain, token: localStorage.getItem("user_token") })
stream.on("data", (rsp) => {
if (rsp.hasHit()) {
let hit = rsp.getHit()
// display the value in the console...
hit.getSnippetsList().forEach(val => {
if (hit.getTitle().getType() == "TVEpisode") {
let episode = hit.getTitle()
episode.globule = globule
episodes.push(episode)
}
})
}
});
stream.on("status", (status) => {
if (status.code == 0) {
// Here I will sort the episodes by seasons and episodes.
callback(episodes.sort((a, b) => {
if (a.getSeason() === b.getSeason()) {
// Price is only important when cities are the same
return a.getEpisode() - b.getEpisode();
}
return a.getSeason() - b.getSeason();
}))
}
});
}
/**
* Here I will return the list of asscociated episode for a given series...
* @param {*} indexPath The path to the search engine
* @param {*} title The title
* @param {*} callback The callback with the list of episodes.
*/
export function GetEpisodes(indexPath, title, callback) {
// return the existing list...
if (title.__episodes__ != undefined) {
callback(title.__episodes__)
return
}
searchEpisodes(title.getId(), indexPath, episodes => {
title.__episodes__ = episodes
callback(title.__episodes__)
})
}
/**
* Display information about given object ex. titles, files...
*/
export class InformationsManager extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor() {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
let isShort = this.hasAttribute("short")
this.onclose = null;
// Innitialisation of the layout.
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: var(--palette-background-default);
}
::-webkit-scrollbar-thumb {
background: var(--palette-divider);
}
#container{
display: flex;
flex-direction: column;
padding: 8px;
z-index: 100;
/*
background-color: var(--palette-background-paper);
*/
color: var(--palette-text-primary);
font-size: 1rem;
user-select: none;
max-height: calc(100vh - 100px);
overflow-y: auto;
overflow-x: hidden;
}
#header {
display: flex;
line-height: 20px;
padding-bottom: 10px;
}
#header paper-icon-button {
min-width: 40px;
}
h1 {
font-size: 1.55rem;
}
h2 {
font-size: 1.35rem;
}
h3 {
font-size: 1.25rem;
}
#header h1, h2, h3 {
margin: 5px;
}
.title-div{
display: flex;
flex-direction: column;
flex-grow: 1;
color: var(--palette-text-primery);
border-bottom: 2px solid;
border-color: var(--palette-divider);
width: 66.66%;
margin-bottom: 0px;
user-select: none;
}
.title-sub-title-div{
display: flex;
}
title-sub-title-div span {
padding-right: 5px;
user-select: none;
}
#title-year {
padding-left: 10px;
padding-right: 10px;
}
.permissions{
padding: 10px;
}
</style>
<div id="container">
<div id="header">
<div class="title-div"></div>
<paper-icon-button icon="close" style="${isShort ? "display: none;" : ""}"></paper-icon-button>
</div>
<slot></slot>
</div>
`
// give the focus to the input.
this.container = this.shadowRoot.querySelector("#container")
// The close button...
let closeButton = this.shadowRoot.querySelector("paper-icon-button")
closeButton.onclick = () => {
// remove it from it parent.
this.parentNode.removeChild(this)
if (this.onclose != null) {
this.onclose()
}
}
// Connect event here...
let globules = Model.getGlobules()
let subscribe = (index) => {
let globule = globules[index]
index++
globule.eventHub.subscribe("delete_audio_event", uuid => { }, evt => {
let id = "_" + getUuidByString(evt)
if (this.querySelector("#" + id)) {
if (this.parentNode)
this.parentNode.removeChild(this)
}
}, false)
globule.eventHub.subscribe("delete_video_event", uuid => { }, evt => {
let id = "_" + getUuidByString(evt)
if (this.querySelector("#" + id)) {
if (this.parentNode)
this.parentNode.removeChild(this)
}
}, false)
globule.eventHub.subscribe("delete_title_event", uuid => { }, evt => {
let id = "_" + getUuidByString(evt)
if (this.querySelector("#" + id)) {
if (this.parentNode)
this.parentNode.removeChild(this)
}
}, false)
if (index < globules.length) {
subscribe(index)
}
}
if (globules > 0) {
let index = 0
subscribe(index)
}
}
hideHeader() {
if (this.shadowRoot.querySelector("paper-icon-button"))
this.shadowRoot.querySelector("paper-icon-button").style.display = "none"
if (this.shadowRoot.querySelector("#title-name"))
this.shadowRoot.querySelector("#title-name").style.display = "none"
}
/**
* Display video informations.
* @param {*} videos
*/
setAudiosInformation(audios) {
this.innerHTML = "" // remove previous content.
this.shadowRoot.querySelector(".title-div").innerHTML = ""
let audio = audios[0]
let audioInfo = new AudioInfo(this.shadowRoot.querySelector(".title-div"), this.hasAttribute("short"))
audioInfo.ondelete = () => {
if (this.parentNode)
this.parentNode.removeChild(this)
}
audioInfo.id = "_" + getUuidByString(audio.getId())
audioInfo.setAudio(audio)
this.appendChild(audioInfo)
}
/**
* Display video informations.
* @param {*} videos
*/
setVideosInformation(videos) {
this.innerHTML = "" // remove previous content.
this.shadowRoot.querySelector(".title-div").innerHTML = ""
let video = videos[0]
let videoInfo = new VideoInfo(this.shadowRoot.querySelector(".title-div"), this.hasAttribute("short"))
videoInfo.ondelete = () => {
if (this.parentNode)
this.parentNode.removeChild(this)
}
videoInfo.id = "_" + getUuidByString(video.getId())
videoInfo.setVideo(video)
this.appendChild(videoInfo)
}
/**
* Display video informations.
* @param {*} blog post
*/
setBlogPostInformation(blogPost) {
this.innerHTML = "" // remove previous content.
this.shadowRoot.querySelector(".title-div").innerHTML = ""
let blogPostInfo = new BlogPostInfo(blogPost)
blogPostInfo.ondelete = () => {
if (this.parentNode)
this.parentNode.removeChild(this)
}
blogPostInfo.id = "_" + getUuidByString(blogPost.getId())
this.appendChild(blogPostInfo)
}
/**
* Display title informations.
* @param {*} titles
*/
setTitlesInformation(titles) {
this.innerHTML = "" // remove previous content.
this.shadowRoot.querySelector(".title-div").innerHTML = ""
if (titles.length == 0) {
/** nothinh to display... */
return;
}
let title = null;
if (titles.length == 1) {
title = titles[0]
} else {
titles.forEach(t => {
if (t.getType() == "TVSeries") {
title = t;
}
})
}
if (title == null) {
title = titles[titles.length - 1]
}
let titleInfo = new TitleInfo(this.shadowRoot.querySelector(".title-div"), this.hasAttribute("short"), title.globule)
titleInfo.ondelete = () => {
if (this.parentNode)
this.parentNode.removeChild(this)
}
titleInfo.id = "_" + getUuidByString(title.getId())
titleInfo.setTitle(title)
this.appendChild(titleInfo)
}
setFileInformation(file) {
this.innerHTML = "" // remove previous content.
this.shadowRoot.querySelector(".title-div").innerHTML = `
<div style="display: flex; align-items: center;">
<iron-icon id="icon" icon="icons:info"> </iron-icon>
<span style="flex-grow: 1; padding-left: 20px; font-size: 1.1rem; user-select: none;">${file.name} <span style="color: var(--palette-text-secondary); margin-left: 16px; user-select: none;">Properties</span></span>
</div>`
let fileInfo = new FileInfo(file)
this.appendChild(fileInfo)
}
}
customElements.define('globular-informations-manager', InformationsManager)
/**
* Modify Audio Informations
*/
export class AudioInfoEditor extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor(audio, audioInfosDisplay) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
let imageUrl = "" // in case the video dosent contain any poster info...
if (audio.getPoster())
imageUrl = audio.getPoster().getContenturl()
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
#container {
display: flex;
margin-top: 15px;
margin-bottom: 15px;
}
.action-div{
display: flex;
justify-content: end;
border-top: 2px solid;AudioInfoEditor
border-color: var(--palette-divider);
}
.button-div{
display: table-cell;
vertical-align: top;
}
.label{
font-size: 1rem;
padding-right: 10px;
min-width: 150px;
}
div, paper-input, iron-autogrow-textarea {
font-size: 1rem;
}
paper-button {AudioInfoEditor
font-size: 1rem;
}
a {
color: var(--palette-divider);
}
select {
background: var(--palette-background-default);
color: var(--palette-text-accent);
border:0px;
outline:0px;
}
</style>
<div id="container">
<div style="display: flex; flex-direction: column; justify-content: flex-start; margin-left: 15px;">
<div style="display: flex; flex-direction: column; margin: 5px;">
<globular-image-selector label="cover" url="${imageUrl}"></globular-image-selector>
</div>
</div>
<div style="display: flex; flex-direction: column; width: 100%;">
<div style="display: table; flex-grow: 1; margin-left: 20px; border-collapse: collapse; margin-top: 20px; margin-bottom: 10px;">
<div style="display: table-row; border-bottom: 1px solid var(--palette-divider)" >
<div class="label" style="display: table-cell; font-weight: 450; border-bottom: 1px solid var(--palette-divider)">Audio Informations</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Id:</div>
<div style="display: table-cell; width: 100%;" id="audio-id-div">${audio.getId()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getId()}" id="audio-id-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-id-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Url:</div>
<div style="display: table-cell; width: 100%;" id="audio-url-div">${audio.getUrl()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getUrl()}" id="audio-url-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-url-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Title:</div>
<div style="display: table-cell; width: 100%;" id="audio-title-div">${audio.getTitle()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getTitle()}" id="audio-title-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-title-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Artist:</div>
<div style="display: table-cell; width: 100%;" id="audio-artist-div">${audio.getArtist()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getArtist()}" id="audio-artist-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-artist-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Album Artist:</div>
<div style="display: table-cell; width: 100%;" id="audio-album-artist-div">${audio.getAlbumartist()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getAlbumartist()}" id="audio-album-artist-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-album-artist-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Composer:</div>
<div style="display: table-cell; width: 100%;" id="audio-composer-div">${audio.getComposer()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getComposer()}" id="audio-composer-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-composer-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; ">Album:</div>
<div style="display: table-cell; width: 100%;" id="audio-album-div">${audio.getAlbum()}</div>
<paper-input style="display: none; width: 100%;" value="${audio.getAlbum()}" id="audio-album-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-album-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; vertical-align: top;">Comment:</div>
<div id="audio-comment-div" style="display: table-cell;width: 100%; padding-bottom: 10px;" >${audio.getComment()}</div>
<iron-autogrow-textarea id="audio-comment-input" style="display: none; border: none; width: 100%;" value="${audio.getComment()}"></iron-autogrow-textarea>
<div class="button-div">
<paper-icon-button id="edit-audio-comment-btn" style="vertical-align: top;" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450; vertical-align: top;">Lyrics:</div>
<div id="audio-lyrics-div" style="display: table-cell;width: 100%; padding-bottom: 10px;" >${audio.getLyrics()}</div>
<iron-autogrow-textarea id="audio-lyrics-input" style="display: none; border: none; width: 100%;" value="${audio.getLyrics()}"></iron-autogrow-textarea>
<div class="button-div">
<paper-icon-button id="edit-audio-lyrics-btn" style="vertical-align: top;" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;" id="audio-year-row">
<div class="label" style="display: table-cell; font-weight: 450; ">Year:</div>
<div style="display: table-cell; width: 100%;" id="audio-year-div">${audio.getYear()}</div>
<paper-input style="display: none; width: 100%;" type="number" value="${audio.getYear()}" id="audio-year-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-year-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;" id="audio-disc-number-row">
<div class="label" style="display: table-cell; font-weight: 450;">Disc Number:</div>
<div style="display: table-cell; width: 100%;" id="audio-disc-number-div">${audio.getDiscnumber()}</div>
<paper-input style="display: none; width: 100%;" type="number" value="${audio.getDiscnumber()}" id="audio-disc-number-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-disc-number-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;" id="audio-disc-total-row">
<div class="label" style="display: table-cell; font-weight: 450;">Disc Total:</div>
<div style="display: table-cell; width: 100%;" id="audio-disc-total-div">${audio.getDisctotal()}</div>
<paper-input style="display: none; width: 100%;" type="number" value="${audio.getDisctotal()}" id="audio-disc-total-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-disc-total-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;" id="audio-track-number-row">
<div class="label" style="display: table-cell; font-weight: 450;">Track Number:</div>
<div style="display: table-cell; width: 100%;" id="audio-track-number-div">${audio.getTracknumber()}</div>
<paper-input style="display: none; width: 100%;" type="number" value="${audio.getTracknumber()}" id="audio-track-number-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-track-number-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;" id="audio-track-total-row">
<div class="label" style="display: table-cell; font-weight: 450;">Track Total:</div>
<div style="display: table-cell; width: 100%;" id="audio-track-total-div">${audio.getTracktotal()}</div>
<paper-input style="display: none; width: 100%;" type="number" value="${audio.getTracktotal()}" id="audio-track-total-input" no-label-float></paper-input>
<div class="button-div">
<paper-icon-button id="edit-audio-track-total-btn" icon="image:edit"></paper-icon-button>
</div>
</div>
<div style="display: table-row;">
<div class="label" style="display: table-cell; font-weight: 450;">Genres:</div>
<div id="audio-genres-div" style="display: table-cell; width: 100%;"></div>
</div>
</div>
</div>
</div>
<iron-collapse class="permissions" id="collapse-panel" style="display: flex; flex-direction: column; margin: 5px;">
</iron-collapse>
<div class="action-div" style="${this.isShort ? "display: none;" : ""}">
<paper-button id="edit-permissions-btn" audio="set who can edit this audio informations">Permissions</paper-button>
<span style="flex-grow: 1;"></span>
<paper-button id="save-indexation-btn">Save</paper-button>
<paper-button id="cancel-indexation-btn">Cancel</paper-button>
</div>
`
this.shadowRoot.querySelector("#cancel-indexation-btn").onclick = () => {
let parent = this.parentNode
parent.removeChild(this)
parent.appendChild(audioInfosDisplay)
}
// Delete the postser/cover image.
this.shadowRoot.querySelector("globular-image-selector").ondelete = () => {
audio.getPoster().setContenturl("")
}
// Select new image.
this.shadowRoot.querySelector("globular-image-selector").onselectimage = (imageUrl) => {
if (audio.getPoster() == null) {
let poster = new Poster()
audio.setPoster(poster)
}
audio.getPoster().setContenturl(imageUrl)
}
let audioGenresDiv = this.shadowRoot.querySelector("#audio-genres-div")
let audioGenresList = new EditableStringList(audio.getGenresList())
audioGenresDiv.appendChild(audioGenresList)
let editPemissionsBtn = this.shadowRoot.querySelector("#edit-permissions-btn")
let collapse_panel = this.shadowRoot.querySelector("#collapse-panel")
this.permissionManager = new PermissionsManager()
this.permissionManager.permissions = null
this.permissionManager.globule = audio.globule
this.permissionManager.setPath(audio.getId())
this.permissionManager.setResourceType = "audio_info"
// toggle the collapse panel when the permission manager panel is close.
this.permissionManager.onclose = () => {
collapse_panel.toggle();
}
// I will display the permission manager.
editPemissionsBtn.onclick = () => {
collapse_panel.appendChild(this.permissionManager)
collapse_panel.toggle();
}
// Here I will set the interaction...
this.shadowRoot.querySelector("#cancel-indexation-btn").onclick = () => {
let parent = this.parentNode
parent.removeChild(this)
parent.appendChild(audioInfosDisplay)
}
// Delete the postser/cover image.
this.shadowRoot.querySelector("globular-image-selector").ondelete = () => {
audio.getPoster().setContenturl("")
}
// Select new image.
this.shadowRoot.querySelector("globular-image-selector").onselectimage = (imageUrl) => {
if (audio.getPoster() == null) {
let poster = new Poster()
audio.setPoster(poster)
}
audio.getPoster().setContenturl(imageUrl)
}
// The audio title
let editAudioTitleBtn = this.shadowRoot.querySelector("#edit-audio-title-btn")
let audioTitleInput = this.shadowRoot.querySelector("#audio-title-input")
let audioTitleDiv = this.shadowRoot.querySelector("#audio-title-div")
editAudioTitleBtn.onclick = () => {
audioTitleInput.style.display = "table-cell"
audioTitleDiv.style.display = "none"
setTimeout(() => {
audioTitleInput.focus()
audioTitleInput.inputElement.inputElement.select()
}, 100)
}
audioTitleInput.onblur = () => {
audioTitleInput.style.display = "none"
audioTitleDiv.style.display = "table-cell"
audioTitleDiv.innerHTML = audioTitleInput.value
}
// The audio id
let audioIdBtn = this.shadowRoot.querySelector("#edit-audio-id-btn")
let audioIdInput = this.shadowRoot.querySelector("#audio-id-input")
let audioIdDiv = this.shadowRoot.querySelector("#audio-id-div")
audioIdBtn.onclick = () => {
audioIdInput.style.display = "table-cell"
audioIdDiv.style.display = "none"
setTimeout(() => {
audioIdInput.focus()
audioIdInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioIdInput.onblur = () => {
audioIdInput.style.display = "none"
audioIdDiv.style.display = "table-cell"
audioIdDiv.innerHTML = audioIdInput.value
}
// The audio url
let audioUrlBtn = this.shadowRoot.querySelector("#edit-audio-url-btn")
let audioUrlInput = this.shadowRoot.querySelector("#audio-url-input")
let audioUrlDiv = this.shadowRoot.querySelector("#audio-url-div")
audioUrlBtn.onclick = () => {
audioUrlInput.style.display = "table-cell"
audioUrlDiv.style.display = "none"
setTimeout(() => {
audioUrlInput.focus()
audioUrlInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioUrlInput.onblur = () => {
audioUrlInput.style.display = "none"
audioUrlDiv.style.display = "table-cell"
audioUrlDiv.innerHTML = audioUrlInput.value
}
// The artist
let audioArtistBtn = this.shadowRoot.querySelector("#edit-audio-artist-btn")
let audioArtistInput = this.shadowRoot.querySelector("#audio-artist-input")
let audioArtistDiv = this.shadowRoot.querySelector("#audio-artist-div")
audioArtistBtn.onclick = () => {
audioArtistInput.style.display = "table-cell"
audioArtistDiv.style.display = "none"
setTimeout(() => {
audioArtistInput.focus()
audioArtistInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioArtistInput.onblur = () => {
audioArtistInput.style.display = "none"
audioArtistDiv.style.display = "table-cell"
audioArtistDiv.innerHTML = audioArtistInput.value
}
// The composer
let audioComposerBtn = this.shadowRoot.querySelector("#edit-audio-composer-btn")
let audioComposerInput = this.shadowRoot.querySelector("#audio-composer-input")
let audioComposerDiv = this.shadowRoot.querySelector("#audio-composer-div")
audioComposerBtn.onclick = () => {
audioComposerInput.style.display = "table-cell"
audioComposerDiv.style.display = "none"
setTimeout(() => {
audioComposerInput.focus()
audioComposerInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioComposerInput.onblur = () => {
audioComposerInput.style.display = "none"
audioComposerDiv.style.display = "table-cell"
audioComposerDiv.innerHTML = audioComposerInput.value
}
// The album
let audioAlbumBtn = this.shadowRoot.querySelector("#edit-audio-album-btn")
let audioAlbumInput = this.shadowRoot.querySelector("#audio-album-input")
let audioAlbumDiv = this.shadowRoot.querySelector("#audio-album-div")
audioAlbumBtn.onclick = () => {
audioAlbumInput.style.display = "table-cell"
audioAlbumDiv.style.display = "none"
setTimeout(() => {
audioAlbumInput.focus()
audioAlbumInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioAlbumInput.onblur = () => {
audioAlbumInput.style.display = "none"
audioAlbumDiv.style.display = "table-cell"
audioAlbumDiv.innerHTML = audioAlbumInput.value
}
// The album artist
let audioAlbumArtistBtn = this.shadowRoot.querySelector("#edit-audio-album-artist-btn")
let audioAlbumArtistInput = this.shadowRoot.querySelector("#audio-album-artist-input")
let audioAlbumArtistDiv = this.shadowRoot.querySelector("#audio-album-artist-div")
audioAlbumArtistBtn.onclick = () => {
audioAlbumArtistInput.style.display = "table-cell"
audioAlbumArtistDiv.style.display = "none"
setTimeout(() => {
audioAlbumArtistInput.focus()
audioAlbumArtistInput.inputElement.inputElement.select()
}, 100)
}
audioAlbumArtistInput.onblur = () => {
audioAlbumArtistInput.style.display = "none"
audioAlbumArtistDiv.style.display = "table-cell"
audioAlbumArtistDiv.innerHTML = audioAlbumArtistInput.value
}
// The audio comment
let editAudioCommentBtn = this.shadowRoot.querySelector("#edit-audio-comment-btn")
let audioCommentInput = this.shadowRoot.querySelector("#audio-comment-input")
let audioCommentDiv = this.shadowRoot.querySelector("#audio-comment-div")
editAudioCommentBtn.onclick = () => {
audioCommentInput.style.display = "table-cell"
audioCommentDiv.style.display = "none"
setTimeout(() => {
audioCommentInput.focus()
audioCommentInput.textarea.select()
}, 100)
}
// set back to non edit mode.
audioCommentInput.onblur = () => {
audioCommentInput.style.display = "none"
audioCommentDiv.style.display = "table-cell"
audioCommentDiv.innerHTML = audioCommentInput.value
}
// The audio lyrics
let editAudioLyricsBtn = this.shadowRoot.querySelector("#edit-audio-lyrics-btn")
let audioLyricsInput = this.shadowRoot.querySelector("#audio-lyrics-input")
let audioLyricsDiv = this.shadowRoot.querySelector("#audio-lyrics-div")
editAudioLyricsBtn.onclick = () => {
audioLyricsInput.style.display = "table-cell"
audioLyricsDiv.style.display = "none"
setTimeout(() => {
audioLyricsInput.focus()
audioLyricsInput.textarea.select()
}, 100)
}
// set back to non edit mode.
audioLyricsInput.onblur = () => {
audioLyricsInput.style.display = "none"
audioLyricsDiv.style.display = "table-cell"
audioLyricsDiv.innerHTML = audioLyricsInput.value
}
// The disc number
let audioDiscNumberBtn = this.shadowRoot.querySelector("#edit-audio-disc-number-btn")
let audioDiscNumberInput = this.shadowRoot.querySelector("#audio-disc-number-input")
let audioDiscNumberDiv = this.shadowRoot.querySelector("#audio-disc-number-div")
audioDiscNumberBtn.onclick = () => {
audioDiscNumberInput.style.display = "table-cell"
audioDiscNumberDiv.style.display = "none"
setTimeout(() => {
audioDiscNumberInput.focus()
audioDiscNumberInput.inputElement.inputElement.select()
}, 100)
}
audioDiscNumberInput.onblur = () => {
audioDiscNumberInput.style.display = "none"
audioDiscNumberDiv.style.display = "table-cell"
audioDiscNumberDiv.innerHTML = audioDiscNumberInput.value
}
// The disc total
let audioDiscTotalBtn = this.shadowRoot.querySelector("#edit-audio-disc-total-btn")
let audioDiscTotalInput = this.shadowRoot.querySelector("#audio-disc-total-input")
let audioDiscTotalDiv = this.shadowRoot.querySelector("#audio-disc-total-div")
audioDiscTotalBtn.onclick = () => {
audioDiscTotalInput.style.display = "table-cell"
audioDiscTotalDiv.style.display = "none"
setTimeout(() => {
audioDiscTotalInput.focus()
audioDiscTotalInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioDiscTotalInput.onblur = () => {
audioDiscTotalInput.style.display = "none"
audioDiscTotalDiv.style.display = "table-cell"
audioDiscTotalDiv.innerHTML = audioDiscTotalInput.value
}
// The track number
let audioTrackNumberBtn = this.shadowRoot.querySelector("#edit-audio-track-number-btn")
let audioTrackNumberInput = this.shadowRoot.querySelector("#audio-track-number-input")
let audioTrackNumberDiv = this.shadowRoot.querySelector("#audio-track-number-div")
audioTrackNumberBtn.onclick = () => {
audioTrackNumberInput.style.display = "table-cell"
audioTrackNumberDiv.style.display = "none"
setTimeout(() => {
audioTrackNumberInput.focus()
audioTrackNumberInput.inputElement.inputElement.select()
}, 100)
}
audioTrackNumberInput.onblur = () => {
audioTrackNumberInput.style.display = "none"
audioTrackNumberDiv.style.display = "table-cell"
audioTrackNumberDiv.innerHTML = audioTrackNumberInput.value
}
// The track total
let audioTrackTotalBtn = this.shadowRoot.querySelector("#edit-audio-track-total-btn")
let audioTrackTotalInput = this.shadowRoot.querySelector("#audio-track-total-input")
let audioTrackTotalDiv = this.shadowRoot.querySelector("#audio-track-total-div")
audioTrackTotalBtn.onclick = () => {
audioTrackTotalInput.style.display = "table-cell"
audioTrackTotalDiv.style.display = "none"
setTimeout(() => {
audioTrackTotalInput.focus()
audioTrackTotalInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioTrackTotalInput.onblur = () => {
audioTrackTotalInput.style.display = "none"
audioTrackTotalDiv.style.display = "table-cell"
audioTrackTotalDiv.innerHTML = audioTrackTotalInput.value
}
// The audio year
let audioYearBtn = this.shadowRoot.querySelector("#edit-audio-year-btn")
let audioYearInput = this.shadowRoot.querySelector("#audio-year-input")
let audioYearDiv = this.shadowRoot.querySelector("#audio-year-div")
audioYearBtn.onclick = () => {
audioYearInput.style.display = "table-cell"
audioYearDiv.style.display = "none"
setTimeout(() => {
audioYearInput.focus()
audioYearInput.inputElement.inputElement.select()
}, 100)
}
// set back to non edit mode.
audioYearInput.onblur = () => {
audioYearInput.style.display = "none"
audioYearDiv.style.display = "table-cell"
audioYearDiv.innerHTML = audioYearInput.value
}
// set back the audio.
this.shadowRoot.querySelector("#save-indexation-