UNPKG

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
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-