PageSourceSearch

https://cdn.karsan.com/Delivery/Public/Theme/default/karsan/video-ytp-module.js

js karsan.com collected 2026-10-02 16:46:03 UTC 17,485 bytes, 389 lines download raw bytes

1/**
2 * Video Gallery Module
3 * 
4 * Kullanım:
5 *   <div class="video-gallery-container"
6 *        data-playlist-id="PLxxxxxx"     ← YouTube Playlist ID
7 *        data-api-key="YOUR_API_KEY"     ← YouTube Data API v3 Key
8 *        ...>
9 * 
10 * Çoklu instance: Aynı sayfada birden fazla .video-gallery-container olabilir,
11 * her biri tamamen bağımsız çalışır.
12 * 
13 * Fallback: data-playlist-id yoksa HTML'deki statik .playlist-item öğeleri okunur.
14 */
15
16// ─────────────────────────────────────────────────────────────────────────────
17// 1. YouTube IFrame API'yi tek seferlik yükle
18// ─────────────────────────────────────────────────────────────────────────────
19(function () {
20    if (window._ytApiLoading) return; // Zaten yükleniyor, tekrar ekleme
21    window._ytApiLoading = true;
22
23    var tag = document.createElement('script');
24    tag.src = 'https://www.youtube.com/iframe_api';
25    document.head.appendChild(tag);
26})();
27
28// Henüz hazır olmayan instance'ları tutacak kuyruk
29window._ytGalleryQueue = window._ytGalleryQueue || [];
30
31// ─────────────────────────────────────────────────────────────────────────────
32// 2. YouTube IFrame API hazır olduğunda tüm instance'ları başlat
33// ─────────────────────────────────────────────────────────────────────────────
34window.onYouTubeIframeAPIReady = function () {
35    // Kuyruktaki instance'ları başlat
36    window._ytGalleryQueue.forEach(function (gallery) {
37        gallery._createPlayer();
38    });
39    window._ytGalleryQueue = [];
40    window._ytApiReady = true;
41};
42
43// ─────────────────────────────────────────────────────────────────────────────
44// 3. VideoGallery Sınıfı
45// ─────────────────────────────────────────────────────────────────────────────
46function VideoGallery(containerEl) {
47    this.container  = containerEl;
48    this.player     = null;
49    this.videoList  = [];
50    this.currentIndex = 0;
51
52    // Her instance için benzersiz ID oluştur
53    this.uid = 'ytg-' + Math.random().toString(36).substr(2, 8);
54
55    // Config'i data attribute'lardan oku
56    this.apiKey     = containerEl.getAttribute('data-api-key')     || '';
57    this.playlistId = containerEl.getAttribute('data-playlist-id') || '';
58
59    this.textConfig = {
60        playing  : containerEl.getAttribute('data-text-playing')   || '► Şu an Çalıyor',
61        start    : containerEl.getAttribute('data-text-start')      || 'Başlatmak için tıklayın',
62        next     : containerEl.getAttribute('data-text-next')       || 'Sıradaki Video',
63        notFound : containerEl.getAttribute('data-text-not-found')  || 'Oynatılacak video bulunamadı.',
64        select   : containerEl.getAttribute('data-text-select')     || 'Bir video seçin...'
65    };
66
67    // Container'ın içindeki elemanlara referanslar
68    this.$container    = $(containerEl);
69    this.$playerWrap   = this.$container.find('.video-player-wrapper');
70    this.$playlistWrap = this.$container.find('.playlist-wrapper');
71    this.$playlistItems= this.$container.find('#playlist-items');
72    this.$videoTitle   = this.$container.find('#video-title');
73    this.$count        = this.$container.find('#playlist-count');
74
75    // Player container'ına benzersiz ID ver (çakışma önleme)
76    this.playerContainerId = 'yt-player-' + this.uid;
77    this.$container.find('#yt-video-player-container')
78                   .attr('id', this.playerContainerId);
79
80    // playlist-items ve diğer ID'leri de benzersiz yap
81    var itemsId = 'playlist-items-' + this.uid;
82    this.$playlistItems.attr('id', itemsId);
83    this.$playlistItems = this.$container.find('#' + itemsId);
84}
85
86// ─── init ────────────────────────────────────────────────────────────────────
87VideoGallery.prototype.init = function () {
88    var self = this;
89
90    if (this.playlistId && this.apiKey) {
91        // Playlist ID varsa API'den çek
92        this._showLoading();
93        this._fetchPlaylist(this.playlistId, null, function () {
94            self._hideLoading();
95            self._setup();
96        });
97    } else {
98        // Statik DOM'dan oku (eski yöntem)
99        this._parseFromDOM();
100        this._setup();
101    }
102};
103
104// ─── YouTube API'den playlist çek (pagination destekli) ──────────────────────
105VideoGallery.prototype._fetchPlaylist = function (playlistId, pageToken, callback) {
106    var self = this;
107    var url = 'https://www.googleapis.com/youtube/v3/playlistItems'
108        + '?part=snippet'
109        + '&maxResults=50'
110        + '&playlistId=' + encodeURIComponent(playlistId)
111        + '&key=' + encodeURIComponent(this.apiKey)
112        + (pageToken ? '&pageToken=' + pageToken : '');
113
114    $.ajax({
115        url: url,
116        type: 'GET',
117        success: function (data) {
118            if (data.items) {
119                data.items.forEach(function (item, i) {
120                    var snippet  = item.snippet;
121                    var videoId  = snippet.resourceId && snippet.resourceId.videoId;
122                    if (!videoId) return; // Silinmiş/private video
123
124                    // Thumbnail: mevcut en iyi kaliteyi seç
125                    var thumb = (snippet.thumbnails.medium  && snippet.thumbnails.medium.url)
126                             || (snippet.thumbnails.default && snippet.thumbnails.default.url)
127                             || 'https://img.youtube.com/vi/' + videoId + '/mqdefault.jpg';
128
129                    self.videoList.push({
130                        index   : self.videoList.length,
131                        title   : snippet.title,
132                        url     : 'https://www.youtube.com/watch?v=' + videoId,
133                        videoId : videoId,
134                        thumb   : thumb
135                    });
136                });
137            }
138
139            // Daha fazla sayfa var mı?
140            if (data.nextPageToken) {
141                self._fetchPlaylist(playlistId, data.nextPageToken, callback);
142            } else {
143                self._buildPlaylistDOM();
144                callback();
145            }
146        },
147        error: function (xhr) {
148            console.error('[VideoGallery] Playlist fetch hatası:', xhr.responseText);
149            self._hideLoading();
150            self.$videoTitle.text('Playlist yüklenemedi. API Key veya Playlist ID\'yi kontrol edin.');
151        }
152    });
153};
154
155// ─── Playlist DOM'unu oluştur (API'den gelen verilerle) ──────────────────────
156VideoGallery.prototype._buildPlaylistDOM = function () {
157    var self = this;
158    this.$playlistItems.empty();
159
160    this.videoList.forEach(function (video) {
161        var $item = $(
162            '<a href="javascript:void(0)" class="list-group-item playlist-item">' +
163                '<div class="media">' +
164                    '<div class="media-left">' +
165                        '<img class="media-object" src="' + video.thumb + '" alt="Thumbnail" style="width:60px;border-radius:4px;">' +
166                    '</div>' +
167                    '<div class="media-body">' +
168                        '<h5 class="media-heading">' + self._escHtml(video.title) + '</h5>' +
169                        '<small>' + self.textConfig.next + '</small>' +
170                    '</div>' +
171                '</div>' +
172            '</a>'
173        );
174        $item.attr('data-index', video.index);
175        $item.attr('data-title', video.title);
176        $item.attr('data-url', video.url);
177        $item.attr('data-video-id', video.videoId);
178        self.$playlistItems.append($item);
179    });
180};
181
182// ─── Statik DOM'dan video listesini oku (fallback) ───────────────────────────
183VideoGallery.prototype._parseFromDOM = function () {
184    var self = this;
185    this.videoList = [];
186    this.$playlistItems.find('.playlist-item').each(function (index) {
187        var $this  = $(this);
188        var url    = $this.data('url') || '';
189        var videoId = self._getVideoId(url);
190        var thumb  = $this.find('img').attr('src')
191                  || 'https://img.youtube.com/vi/' + videoId + '/mqdefault.jpg';
192
193        self.videoList.push({
194            index   : index,
195            title   : $this.data('title') || '',
196            url     : url,
197            videoId : videoId,
198            thumb   : thumb,
199            element : $this
200        });
201        $this.attr('data-index', index);
202    });
203};
204
205// ─── Player ve UI hazırla ────────────────────────────────────────────────────
206VideoGallery.prototype._setup = function () {
207    if (this.videoList.length === 0) {
208        this.$videoTitle.text(this.textConfig.notFound);
209        this.$playerWrap.hide();
210        this.$playlistWrap.hide();
211        return;
212    }
213
214    // Tek video modunda playlist panelini gizle
215    if (this.videoList.length <= 1) {
216        this.$playlistWrap.hide();
217        this.$playerWrap.removeClass('col-md-8').addClass('col-md-12');
218    } else {
219        this.$playlistWrap.show();
220        this.$count.text(this.videoList.length);
221    }
222
223    this.$videoTitle.text(this.videoList[0].title);
224    this._updateActiveItem(0, false);
225
226    // Player oluştur
227    if (window._ytApiReady) {
228        this._createPlayer();
229    } else {
230        // API henüz hazır değil, kuyruğa ekle
231        window._ytGalleryQueue.push(this);
232    }
233
234    // Tıklama event'ı — event delegation, instance'a özel
235    this._bindEvents();
236};
237
238// ─── YT.Player oluştur ───────────────────────────────────────────────────────
239VideoGallery.prototype._createPlayer = function () {
240    var self = this;
241    var firstVideoId = this.videoList[0] && this.videoList[0].videoId;
242
243    setTimeout(function () {
244        try {
245            self.player = new YT.Player(self.playerContainerId, {
246                height    : '100%',
247                width     : '100%',
248                videoId   : firstVideoId,
249                playerVars: {
250                    autoplay       : 0,
251                    rel            : 0,
252                    modestbranding : 1,
253                    controls       : 1
254                },
255                events: {
256                    onReady      : function () { console.log('[VideoGallery] Player hazır:', self.uid); },
257                    onStateChange: function (e) { self._onStateChange(e); },
258                    onError      : function (e) { self._onPlayerError(e); }
259                }
260            });
261        } catch (err) {
262            console.error('[VideoGallery] Player oluşturulamadı:', err);
263        }
264    }, 100);
265};
266
267// ─── Video yükle ─────────────────────────────────────────────────────────────
268VideoGallery.prototype._loadVideo = function (index) {
269    if (index < 0 || index >= this.videoList.length) return;
270
271    this.currentIndex = index;
272    var video = this.videoList[index];
273
274    if (this.player && typeof this.player.loadVideoById === 'function') {
275        this.player.loadVideoById(video.videoId);
276    }
277
278    this.$videoTitle.text(video.title);
279    this._updateActiveItem(index, true);
280};
281
282// ─── Sıradaki videoya geç ────────────────────────────────────────────────────
283VideoGallery.prototype._onStateChange = function (event) {
284    if (event.data === YT.PlayerState.ENDED) {
285        var next = this.currentIndex + 1;
286        if (next < this.videoList.length) {
287            this._loadVideo(next);
288        }
289    }
290};
291
292// ─── Hata yakalama — embed izni yoksa sıradakine geç ────────────────────────
293VideoGallery.prototype._onPlayerError = function (event) {
294    // 101, 150 → video sahibi embedding'e izin vermiyor
295    // 100      → video silinmiş / private
296    // 5        → HTML5 player hatası
297    var skipCodes = [5, 100, 101, 150];
298    if (skipCodes.indexOf(event.data) !== -1) {
299        console.warn('[VideoGallery] Video oynatılamadı (kod ' + event.data + '), sıradakine geçiliyor...');
300        var next = this.currentIndex + 1;
301        var self = this;
302        if (next < this.videoList.length) {
303            this.$videoTitle.text('⏭ Bu video oynatılamıyor, sıradakine geçiliyor...');
304            setTimeout(function () { self._loadVideo(next); }, 1500);
305        } else {
306            this.$videoTitle.text('⚠️ Bu video gömülü oynatmaya izin vermiyor.');
307        }
308    } else {
309        console.error('[VideoGallery] Player hatası (kod: ' + event.data + ')');
310    }
311};
312
313// ─── Aktif playlist öğesini güncelle ─────────────────────────────────────────
314VideoGallery.prototype._updateActiveItem = function (index, isPlaying) {
315    var self = this;
316    this.$playlistItems.find('.playlist-item').each(function () {
317        $(this).removeClass('active');
318        $(this).find('.media-body small').text(self.textConfig.next);
319    });
320
321    var video = this.videoList[index];
322    var $el   = video.element || this.$playlistItems.find('[data-index="' + index + '"]');
323
324    if ($el && $el.length) {
325        $el.addClass('active');
326        $el.find('.media-body small').text(isPlaying ? this.textConfig.playing : this.textConfig.start);
327
328        // Scroll
329        var container   = this.$playlistItems;
330        var itemTop     = $el.position() ? $el.position().top : 0;
331        var scrollTop   = container.scrollTop();
332        var containerTop = container.position() ? container.position().top : 0;
333
334        container.animate({
335            scrollTop: itemTop + scrollTop - containerTop - (container.height() / 2) + 50
336        }, 400);
337    }
338};
339
340// ─── Event binding (instance'a özel, event delegation) ───────────────────────
341VideoGallery.prototype._bindEvents = function () {
342    var self = this;
343    this.$playlistItems.on('click', '.playlist-item', function (e) {
344        e.preventDefault();
345        var idx = parseInt($(this).attr('data-index'), 10);
346        self._loadVideo(idx);
347    });
348};
349
350// ─── Loading state ────────────────────────────────────────────────────────────
351VideoGallery.prototype._showLoading = function () {
352    this.$playlistItems.html(
353        '<div class="gallery-loading">' +
354            '<div class="gallery-spinner"></div>' +
355            '<span>Playlist yükleniyor...</span>' +
356        '</div>'
357    );
358};
359
360VideoGallery.prototype._hideLoading = function () {
361    this.$playlistItems.find('.gallery-loading').remove();
362};
363
364// ─── URL'den Video ID çıkar ───────────────────────────────────────────────────
365VideoGallery.prototype._getVideoId = function (url) {
366    var match = (url || '').match(
367        /(?:youtu\.be\/|youtube\.com\/(?:.*[?&]v=|embed\/|v\/))([a-zA-Z0-9_-]{11})/
368    );
369    return (match && match[1]) ? match[1] : '';
370};
371
372// ─── HTML escape ──────────────────────────────────────────────────────────────
373VideoGallery.prototype._escHtml = function (str) {
374    return String(str)
375        .replace(/&/g, '&amp;')
376        .replace(/</g, '&lt;')
377        .replace(/>/g, '&gt;')
378        .replace(/"/g, '&quot;');
379};
380
381// ─────────────────────────────────────────────────────────────────────────────
382// 4. Sayfa yüklendiğinde tüm container'ları başlat
383// ─────────────────────────────────────────────────────────────────────────────
384$(document).ready(function () {
385    $('.video-gallery-container').each(function () {
386        var gallery = new VideoGallery(this);
387        gallery.init();
388    });
389});

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.