class PandoraScroll extends HTMLElement { constructor() { super() if (this.attributes.src && this.attributes.src.value) { const attributes = parseIframeURL(this.attributes.src.value) Object.keys(attributes).forEach(key => { if (key[0] != '_') { this.setAttribute(key, attributes[key]) } }) } const shadow = this.attachShadow({mode: 'open'}) const link = document.createElement('link') link.rel = 'stylesheet' link.href = document.head.querySelector('link[rel="stylesheet"]').href const style = document.createElement('style') const body = document.createElement('div') body.classList.add('pandora-scroll') shadow.appendChild(link) shadow.appendChild(style) shadow.appendChild(body) window.addEventListener('resize', () => { this._resize() }, false) this.mute = function() { shadow.querySelectorAll('video').forEach(video => { video.muted = true }) } } static get observedAttributes() { return ['muted']; } connectedCallback() { this._loadAnnotations() this._updateStyle() } disconnectedCallback() { //console.log('scroll disconnected') } adoptedCallback() { //console.log('element moved to new page.'); } attributeChangedCallback(name, oldValue, newValue) { //console.log('value changed', name, oldValue, newValue); this._updateStyle() } _updateStyle() { const shadow = this.shadowRoot; const childNodes = shadow.childNodes; for (const node of childNodes) { if (node.nodeName === 'STYLE') { node.textContent = ` .pandora-scroll { position: relative; display: block; } ` } } } _config() { var config = {} for (var i=0; i -1) { config[a.name] = parseTime(a.value) } else { config[a.name] = a.value } } return config } _loadAnnotations() { var config = this._config() config.root = this.shadowRoot.querySelector('.pandora-scroll') config.video = document.querySelector('video') loadAnnotations(config).then(config => { config.annotations.forEach(annotation => { annotation.src = `${streamPrefix}/${annotation.id.split('/')[0]}/480p.webm` renderAnnotation(window.config, config.video, config.root, annotation) }) }) } _resize() { var video = this.shadowRoot.querySelector('video') if (video && video._frame) { var rect = video._frame.getBoundingClientRect(), root_rect = video._root.getBoundingClientRect(), top = rect.top - root_rect.top; video.style.top = top + 'px' } } } class PandoraItem extends HTMLElement { constructor() { super() } } class PandoraEdit extends HTMLElement { constructor() { super() } } customElements.define("pandora-scroll", PandoraScroll); customElements.define("pandora-item", PandoraItem); customElements.define("pandora-edit", PandoraEdit); function parseIframeURL(value) { var config = {} value = value.replace('/player/', '/') value = value.replace('/editor/', '/') var data = value.split('#embed?') function decodeValue(value) { return decodeURIComponent(value) .replace(/_/g, ' ').replace(/\t/g, '_') .replace(/\x0E/g, '<').replace(/\x0F/g, '>'); } var args = data[1].replace('&', '&').split('&').map(kv => { kv = kv.split('=') //console.log(kv, decodeValue(kv[1])) return [kv[0], JSON.parse(decodeValue(kv[1]))] }).reduce((k, v) => { k[v[0]] = v[1] return k }, {}) var parts = data[0].split('/') config.item = parts[3] var ts = parts[4].split(',') config['in'] = ts[0] config['out'] = ts[1] config._args = args if (args.showLayers) { config.layers = args.showLayers.join(' ') } if (args.showTimeline) { config.timeline = "slitscan" } return config }