PageSourceSearch

https://trixbrix.eu/assets/static_page_helpers/editorjs_youtube-e693d1f0.js

js trixbrix.eu collected 2026-10-03 19:51:54 UTC 4,561 bytes, 159 lines download raw bytes

1const toolboxIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2  <rect x="2" y="4" width="20" height="16" rx="4" ry="4"/>
3  <polygon points="10,8 16,12 10,16" fill="currentColor" stroke="none"/>
4</svg>`
5
6export default class YouTube {
7  static get toolbox() {
8    return {
9      title: 'YouTube',
10      icon: toolboxIcon
11    }
12  }
13
14  static get isReadOnlySupported() {
15    return true
16  }
17
18  constructor({ data, config, api, readOnly }) {
19    this.api = api
20    this.readOnly = readOnly
21    this.data = {
22      url: data?.url || '',
23      videoId: data?.videoId || ''
24    }
25  }
26
27  render() {
28    this.wrapper = document.createElement('div')
29    this.wrapper.classList.add('youtube-tool')
30
31    if (this.data.videoId) {
32      this._renderVideo()
33    } else {
34      this._renderInput()
35    }
36
37    return this.wrapper
38  }
39
40  _renderInput() {
41    this.wrapper.innerHTML = ''
42
43    const container = document.createElement('div')
44    container.classList.add('p-4', 'border', 'border-dashed', 'border-gray-300', 'rounded-lg', 'bg-gray-50')
45
46    const label = document.createElement('div')
47    label.classList.add('text-xs', 'text-gray-400', 'mb-2')
48    label.textContent = 'Wklej link do YouTube'
49
50    this.urlInput = document.createElement('input')
51    this.urlInput.type = 'text'
52    this.urlInput.value = this.data.url
53    this.urlInput.placeholder = 'https://www.youtube.com/watch?v=...'
54    this.urlInput.disabled = this.readOnly
55    this.urlInput.classList.add(
56      'w-full', 'border', 'border-gray-200', 'rounded', 'px-3', 'py-2',
57      'text-sm', 'text-gray-700', 'focus:outline-none', 'focus:ring-2', 'focus:ring-blue-500'
58    )
59
60    this.urlInput.addEventListener('paste', (e) => {
61      setTimeout(() => this._processUrl(this.urlInput.value), 0)
62    })
63
64    this.urlInput.addEventListener('keydown', (e) => {
65      if (e.key === 'Enter') {
66        e.preventDefault()
67        this._processUrl(this.urlInput.value)
68      }
69    })
70
71    const button = document.createElement('button')
72    button.type = 'button'
73    button.textContent = 'Dodaj wideo'
74    button.classList.add(
75      'mt-2', 'px-4', 'py-2', 'bg-red-600', 'text-white', 'rounded',
76      'text-sm', 'hover:bg-red-700', 'transition-colors'
77    )
78    button.addEventListener('click', () => this._processUrl(this.urlInput.value))
79
80    container.appendChild(label)
81    container.appendChild(this.urlInput)
82    container.appendChild(button)
83    this.wrapper.appendChild(container)
84  }
85
86  _renderVideo() {
87    this.wrapper.innerHTML = ''
88
89    const container = document.createElement('div')
90    container.classList.add('relative', 'w-full')
91    container.style.aspectRatio = '16 / 9'
92
93    const iframe = document.createElement('iframe')
94    iframe.src = `https://www.youtube.com/embed/${this.data.videoId}`
95    iframe.classList.add('w-full', 'h-full', 'rounded-lg')
96    iframe.setAttribute('frameborder', '0')
97    iframe.setAttribute('allowfullscreen', 'true')
98    iframe.setAttribute('allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture')
99
100    container.appendChild(iframe)
101    this.wrapper.appendChild(container)
102
103    if (!this.readOnly) {
104      const removeBtn = document.createElement('button')
105      removeBtn.type = 'button'
106      removeBtn.innerHTML = '&times; Usuń'
107      removeBtn.classList.add(
108        'mt-2', 'px-3', 'py-1', 'bg-gray-200', 'text-gray-600', 'rounded',
109        'text-xs', 'hover:bg-gray-300', 'transition-colors'
110      )
111      removeBtn.addEventListener('click', () => {
112        this.data.url = ''
113        this.data.videoId = ''
114        this._renderInput()
115      })
116      this.wrapper.appendChild(removeBtn)
117    }
118  }
119
120  _processUrl(url) {
121    const videoId = this._extractVideoId(url)
122    if (videoId) {
123      this.data.url = url
124      this.data.videoId = videoId
125      this._renderVideo()
126    }
127  }
128
129  _extractVideoId(url) {
130    if (!url) return null
131
132    // Match various YouTube URL formats
133    const patterns = [
134      /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
135      /^([a-zA-Z0-9_-]{11})$/ // Just the video ID
136    ]
137
138    for (const pattern of patterns) {
139      const match = url.match(pattern)
140      if (match) return match[1]
141    }
142
143    return null
144  }
145
146  save() {
147    return {
148      url: this.data.url,
149      videoId: this.data.videoId
150    }
151  }
152
153  static get sanitize() {
154    return {
155      url: false,
156      videoId: false
157    }
158  }
159}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.