PageSourceSearch

https://www.imagely.com/wp-content/plugins/nextgen-gallery-pro/sta…/galleria.nextgen_pro_lightbox.js?ver=4.5.1

js imagely.com collected 2026-10-02 02:46:58 UTC 99,126 bytes, 1,718 lines download raw bytes

1(function($) {
2
3
4    var add_nggpl_theme = function() {
5
6        Galleria.addTheme({
7            name: 'nextgen_pro_lightbox',
8            author: 'Imagely',
9            version: 2.0,
10            defaults: {
11                debug: false,
12                responsive: true,
13                carousel: true,
14                thumbnails: 'lazy',
15                fullscreen: false,
16                trueFullscreen: false,
17                fullscreenDoubleTap: false,
18                maxScaleRatio: 1
19            },
20            init: function(options) {
21                var self = this;
22                Galleria.requires(1.41, 'This version of the NextGEN Pro Lightbox theme requires Galleria 1.4.1 or later');
23
24                $.nplModal('log', 'theme initialization', {
25                    options: options
26                });
27
28                // Some objects have aip variables - animation-in-progress - and are used to make jQuery animations smoother
29                var methods = {
30                    sidebar: {
31                        _is_open: false,
32                        _type: '',
33                        is_open: function(state) {
34                            if (typeof state !== 'undefined') {
35                                this._is_open = state;
36                            } else {
37                                return this._is_open;
38                            }
39                        },
40                        toggle: function(type) {
41                            $.nplModal('log', 'theme sidebar.toggle()', {
42                                type: type
43                            });
44                            if (this.is_open() && type === this.get_type()) {
45                                this.close();
46                            } else {
47                                this.open(type);
48                            }
49                        },
50                        open: function(type) {
51                            $.nplModal('log', 'theme sidebar.open()', {
52                                type: type
53                            });
54
55                            $('#npl_wrapper').addClass('npl-sidebar-open npl-sidebar-overlay-open');
56
57                            this.render(type);
58                            methods.sidebar.is_open(true);
59
60                            var state = $.nplModal('get_state');
61                            state.sidebar = type;
62
63                            $.nplModal(
64                                'router.navigate',
65                                $.nplModal('get_setting', 'router_slug')
66                                + '/' + state.slug
67                                + '/' + state.image_id
68                                + '/' + type,
69                                true
70                            );
71
72                            self.resize();
73                            self.trigger('npl_sidebar_opened');
74                        },
75                        close: function() {
76                            $.nplModal('log', 'theme sidebar.close()');
77
78                            $('#npl_wrapper').removeClass('npl-sidebar-open npl-sidebar-overlay-open');
79
80                            methods.sidebar.is_open(false);
81
82                            var state = $.nplModal('get_state');
83                            state.sidebar = null;
84                            $.nplModal(
85                                'router.navigate',
86                                $.nplModal('get_setting', 'router_slug')
87                                + '/' + state.slug
88                                + '/' + state.image_id,
89                                true
90                            );
91
92                            self.resize();
93                            self.trigger('npl_sidebar_closed');
94                        },
95                        render: function(type) {
96                            $.nplModal('log', 'theme sidebar.render()', {
97                                type: type
98                            });
99
100                            // switching to another sidebar type; flash the overlay
101                            if (type !== this.get_type()) {
102                                this._type = type;
103                                $.nplModal('get_state').sidebar = type;
104                            }
105
106                            methods.sidebars[type].render(methods.galleria.get_current_image_id());
107                            methods.sidebars[type].init();
108                        },
109                        get_type: function() {
110                            return this._type;
111                        },
112                        events: {
113                            bind: function() {
114                                $.nplModal('log', 'theme sidebar.events.bind()');
115                                $('#npl_content').on('npl_sidebar_rendered', this.rendered);
116                                $('#npl_sidebar_toggle').on('click', function() {
117                                    methods.sidebar.close();
118                                });
119                            },
120                            rendered: function() {
121                                $.nplModal('log', 'theme sidebar.events.rendered()');
122
123                                // This is hidden except for narrow viewports by default, allows phone users to remember what photo they are 'on'
124                                $('#nggpl-sidebar-thumbnail-img').attr('src', self.getData(self.getIndex()).image);
125                            }
126                        }
127                    },
128                    sidebars: {
129                        datacache: {
130                            _cache: [],
131                            _current_image_ndx: null,
132                            _last_image_ndx: null,
133
134                            events: {
135                                bind: function() {
136                                    $.nplModal('log', 'theme sidebars.datacache.events.bind()');
137                                    self.bind('loadstart', this.loadstart);
138                                },
139
140                                // Used to track which direction users are navigating in so we can intelligently
141                                // pre-load sidebar data in advance of the image being selected
142                                loadstart: function() {
143                                    $.nplModal('log', 'theme sidebars.datacache.events.image()');
144                                    // Initial value at startup
145                                    if (methods.sidebars.datacache._current_image_ndx === null) {
146                                        methods.sidebars.datacache._last_image_ndx = methods.sidebars.datacache._current_image_ndx = self.getIndex();
147                                    } else {
148                                        // Store the current index and shift the old value
149                                        methods.sidebars.datacache._last_image_ndx = methods.sidebars.datacache._current_image_ndx;
150                                        methods.sidebars.datacache._current_image_ndx = self.getIndex();
151                                    }
152                                }
153                            },
154
155                            get_carousel_navigation_direction: function() {
156                                if (this._current_image_ndx === this._last_image_ndx) {
157                                    return 'same';
158                                } else {
159                                    if (this._last_image_ndx === 0 && this._current_image_ndx === (self.getDataLength() - 1)) {
160                                        return 'left';
161                                    } else if (this._current_image_ndx === 0 && this._last_image_ndx === (self.getDataLength() -1)) {
162                                        return 'right';
163                                    } else {
164                                        return (this._current_image_ndx < this._last_image_ndx) ? 'left' : 'right';
165                                    }
166                                }
167                            },
168
169                            // because the .length operator isn't accurate
170                            get_cache_size: function() {
171                                var size = this._cache.length;
172
173                                $.nplModal('log', 'theme sidebars.comments.get_cache_size()', {
174                                    result: size
175                                });
176
177                                return size;
178                            },
179
180                            // returns the image-id field of the first preceeding image found whose comments aren't cached
181                            get_prev_uncached_image_id: function(id, step) {
182                                step = step || 0;
183                                var prev_image_id = self.getData(self.getPrev(methods.galleria.get_index_from_id(id) - step)).image_id;
184                                if (this._cache[prev_image_id] && this.get_cache_size() < self.getDataLength()) {
185                                    prev_image_id = this.get_prev_uncached_image_id(prev_image_id);
186                                }
187
188                                $.nplModal('log', 'theme sidebars.comments.get_prev_uncached_image_id()', {
189                                    id: id,
190                                    result: prev_image_id
191                                });
192
193                                return prev_image_id;
194                            },
195
196                            // returns the image-id field of the first following image found whose comments aren't cached
197                            get_next_uncached_image_id: function(id, step) {
198                                step = step || 0;
199                                var next_image_id = self.getData(self.getNext(methods.galleria.get_index_from_id(id) + step)).image_id;
200                                if (this._cache[next_image_id] && this.get_cache_size() < self.getDataLength()) {
201                                    next_image_id = this.get_next_uncached_image_id(next_image_id);
202                                }
203
204                                $.nplModal('log', 'theme sidebars.comments.get_next_uncached_image_id()', {
205                                    id: id,
206                                    result: next_image_id
207                                });
208
209                                return next_image_id;
210                            },
211
212                            // expanded request method: adds first pre-ceding and following uncached id to the request
213                            expanded_request: function(id, finished) {
214                                $.nplModal('log', 'theme sidebars.comments.expanded_request()', {
215                                    id: id
216                                });
217
218                                var id_array = (id instanceof Array) ? id : id.toString().split(',');
219
220                                // a single ID was requested, so inject some extras so they can be cached in advance
221                                if (id_array.length === 1) {
222                                    var key = id_array[0];
223                                    var prev = this.get_prev_uncached_image_id(key);
224                                    var next = this.get_next_uncached_image_id(key);
225                                    if (!this._cache[prev]) {
226                                        this._cache[prev] = 'Placeholder stub';
227                                        id_array.unshift(prev);
228                                    }
229                                    if (!this._cache[next] && prev !== next && id !== next) {
230                                        this._cache[next] = 'Placeholder stub';
231                                        id_array.push(next);
232                                    }
233                                }
234
235                                id_array = this.sort_uniq(id_array);
236                                this.request(id_array, 0, finished);
237                            },
238
239                            // handles the HTTP request to load comments & cache the results
240                            request: function(id, page, finished, retried) {
241                                $.nplModal('log', 'theme sidebars.comments.request()', {
242                                    id: id,
243                                    page: page,
244                                    retried: retried
245                                });
246
247                                var myself = this; // self is taken
248                                var postdata = {
249                                    type: 'image',
250                                    page: page,
251                                    id: id.join(','),
252                                    from: window.parent.location.toString(),
253                                    // Names the gallery this lightbox is showing, so the
254                                    // server can tell that these image ids are ones the
255                                    // caller was actually served rather than 
255ids they
256                                    // picked -- see Galleria::get_scoped_image_ids().
257                                    gallery_id: $.nplModal('get_state').gallery_id,
258                                };
259                                if ($.nplModal('get_setting', 'lang', false)) {
260                                    postdata.lang = $.nplModal('get_setting', 'lang');
261                                }
262
263                                $.ajax({
264                                    url: photocrati_ajax.rest_url + 'nggpro/v1/Galleria/GetSidebarData',
265                                    method: 'POST',
266                                    timeout: 30000,
267                                    beforeSend: function (xhr) {
268                                        // Read the nonce at send time, not at bind time: the retry below may
269                                        // have refreshed it -- rest_nonce() reads the localized object on
270                                        // every call, so refresh_nonce()'s replacement is picked up here.
271                                        // Logged-in viewers only: a page cache can serve this HTML long
272                                        // after the 12-24h nonce window, and core 403s a stale nonce
273                                        // instead of falling back to anonymous (issue #648).
274                                        var nonce = $.nplModal('rest_nonce');
275                                        if (nonce) {
276                                            xhr.setRequestHeader('X-WP-Nonce', nonce);
277                                        }
278                                    },
279                                    xhrFields: {
280                                        withCredentials: true
281                                    },
282                                    data: postdata,
283                                    success: function(data) {
284                                        $.nplModal('log', 'theme sidebars.comments.request() response', {
285                                            response: data
286                                        });
287
288                                        if (typeof(data) !== 'object') {
289                                            try {
290                                                data = JSON.parse(data);
291                                            } catch (e) {
292                                                myself.request_failed(id, finished, page);
293                                                return;
294                                            }
295                                        }
296                                        for (var ndx in data) {
297                                            myself._cache[ndx] = data[ndx];
298                                        }
299
300                                        // A 200 does not mean every requested id came back: the server omits
301                                        // ids it has no data for. Retire their stubs, or the render loops poll
302                                        // a stub that will never resolve and spin forever (#211).
303                                        myself.mark_no_data(id);
304
305                                        if (typeof finished === 'function') {
306                                            finished(data);
307                                        }
308                                    },
309                                    error: function(jqXHR, status) {
310                                        $.nplModal('log', 'theme sidebars.comments.request() failed', {
311                                            code: jqXHR.status,
312                                            status: status
313                                        });
314
315                                        // A page cache can serve HTML with a nonce older than its ~12-24h
316                                        // lifetime, which WordPress answers with rest_cookie_invalid_nonce.
317                                        // Refresh the nonce and retry once, keeping the visitor's identity --
318                                        // dropping the header instead would render the logged-out sidebar to a
319                                        // logged-in visitor, which is what the nonce was added to prevent.
320                                        // Matched on the error code, not the status, so a WAF's 403 does not
321                                        // earn a second request.
322                                        if (!retried
323                                        &&  jqXHR.status === 403
324                                        &&  jqXHR.responseJSON
325                                        &&  jqXHR.responseJSON.c
325ode === 'rest_cookie_invalid_nonce') {
326                                            myself.refresh_nonce(function() {
327                                                myself.request(id, page, finished, true);
328                                            });
329                                            return;
330                                        }
331
332                                        myself.request_failed(id, finished, page);
333                                    }
334                                });
335                            },
336
337                            // Fetch a fresh wp_rest nonce, then run `next` either way. WordPress answers
338                            // this only for logged-in visitors; a logged-out one gets '0' and keeps no
339                            // nonce, which is correct -- they have no identity to preserve, and the route
340                            // treats a nonce-less request as anonymous.
341                            refresh_nonce: function(next) {
342                                var auth = window.nextgen_pro_lightbox_authentication;
343                                var held = (auth && auth.nonce) ? auth.nonce : '';
344
345                                var apply = function(nonce) {
346                                    if (auth) {
347                                        auth.nonce = nonce;
348                                    }
349                                    $.nplModal('log', 'theme sidebars.comments.refresh_nonce()', {
350                                        refreshed: !!nonce && nonce !== held,
351                                        cleared: !nonce && !!held
352                                    });
353                                    next();
354                                };
355
356                                $.ajax({
357                                    url: this.get_admin_ajax_url(),
358                                    method: 'GET',
359                                    timeout: 10000,
360                                    data: { action: 'rest-nonce' },
361                                    xhrFields: {
362                                        withCredentials: true
363                                    }
364                                }).done(function(response) {
365                                    var nonce = $.trim(String(response));
366
367                                    // '0' / '-1' are WordPress' "no nonce for you" replies: a definite "you
368                                    // are anonymous", so drop the nonce and let the retry go through
369                                    // anonymously -- there is no identity to preserve.
370                                    if (nonce === '0' || nonce === '-1') {
371                                        apply('');
372                                        return;
373                                    }
374
375                                    // Anything else that is not a nonce -- an empty body from a WAF or an
376                                    // edge cache -- says nothing about identity. Keep the one we hold rather
377                                    // than de-identifying the visitor for the rest of the page's life.
378                                    apply(nonce || held);
379                                }).fail(function(jqXHR) {
380                                    var body = $.trim(String(jqXHR.responseText || ''));
381
382                                    // Same rule on the error path: only that explicit reply clears the
383                                    // nonce. A transport failure or an error page keeps it -- responseText
384                                    // is not a nonce.
385                                    apply((body === '0' || body === '-1') ? '' : held);
386                                });
387                            },
388
389                            // admin-ajax.php, which is not reachable under `wp_site_url`: Router appends
390                            // '/index.php' to that on plain-permalink installs, and '/index.php/wp-admin/'
391                            // 404s. `wp_admin_ajax_url` is localized straight from admin_url(); the fallback
392                            // covers a page cached before that setting shipped.
393                            get_admin_ajax_url: function() {
394                                var url = $.nplModal('get_setting', 'wp_admin_ajax_url', '');
395
396                                if (!url) {
397                                    url = String($.nplModal('get_setting', 'wp_site_url', ''))
398                                        .replace(/\/index\.php$/, '') + '/wp-admin/admin-ajax.php';
399                                }
400
401                                return url;
402                            },
403
404                            // Forget any placeholder stub still standing for these ids, so the next render
405                            // re-requests instead of polling a stub that will never resolve. Only for failed
406                            // requests: a successful one that omitted an i
406d uses mark_no_data() instead.
407                            drop_stubs: function(ids) {
408                                for (var i = 0; i < ids.length; i++) {
409                                    if (this._cache[ids[i]] === 'Placeholder stub') {
410                                        delete this._cache[ids[i]];
411                                    }
412                                }
413                            },
414
415                            // Retire the stubs of ids a successful request came back without. Deleting them
416                            // would drop the only record that they were ever asked for -- load_more() reads
417                            // this cache as its "already requested" gate and runs on every image navigation,
418                            // so the same ids would be re-requested for the life of the page. 'No data' is
419                            // truthy, so it holds that gate closed, and both render paths treat it as
420                            // terminal rather than polling it.
421                            mark_no_data: function(ids) {
422                                for (var i = 0; i < ids.length; i++) {
423                                    // Anything the response did not fill in: a stub left by a precache, or
424                                    // an id asked for in the foreground, which is never stubbed. Real
425                                    // payloads are left alone, including a previous page's -- a page-2
426                                    // request the server answered without is no reason to discard page 1.
427                                    if (!this.is_renderable(this._cache[ids[i]])) {
428                                        this._cache[ids[i]] = 'No data';
429                                    }
430                                }
431                            },
432
433                            // Drop everything held for these ids -- stubs, 'No data' markers and stale page
434                            // data alike -- so the next request actually goes out. Used by Retry, which must
435                            // bypass the negative cache the way an automatic render must not.
436                            forget: function(ids) {
437                                for (var i = 0; i < ids.length; i++) {
438                                    delete this._cache[ids[i]];
439                                }
440                            },
441
442                            // a cache entry is renderable only if it is a real payload
443                            is_renderable: function(cache) {
444                                return !!cache && cache !== 'Placeholder stub' && cache !== 'No data';
445                            },
446
447                            // clears the loading overlay and offers a retry; without this the sidebar
448                            // spins forever on any failed request
449                            request_failed: function(ids, finished, page) {
450                                this.drop_stubs(ids);
451
452                                // no callback means a background precache: nothing is waiting on it, so
453                                // leave the sidebar the visitor is already looking at alone
454                                if (typeof finished !== 'function') {
455                                    return;
456                                }
457
458                                this.show_failed(ids, page);
459                            },
460
461                            // The sidebar's terminal fail state: overlay released, message, retry. Retry
462                            // re-issues the request that actually failed -- routing it through
463                            // sidebar.render() instead would lose `page` and, for a failed page-2 fetch,
464                            // silently re-render the cached page 1 without asking the server at all.
465                            show_failed: function(ids, page) {
466                                var myself = this;
467                                var i18n = $.nplModal('get_setting', 'i18n') || {};
468
469                                $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
470                                $('#npl_sidebar').html(
471                                    $('<div class="nggpl-sidebar-error" />')
472                                        .append(
473                                            $('<p />').text(
474                                                i18n.sidebar_load_failed
475                                                || 'This content could not be loaded. Please try again.'
476                                            )
477                                        )
478                                        .append(
479                                            $('<button type="button" class="nggpl-button" />')
480                                                .text(i18n.retry || 'Retry')
481                                                .on('click', function() {
482                                                    myself.retry(ids, page);
483                                                })
484                                        )
485                                );
486                            },
487
488                            // re-request exactly what failed, then re-render whichever sidebar is open
489                            retry: function(ids, page) {
490                                var retry_ids = (ids && ids.length)
491                                    ? ids
492                                    : [methods.galleria.get_current_image_id()];
493
494                                this.forget(retry_ids);
495
496                                $('#npl_wrapper').addClass('npl-sidebar-overlay-open');
497                                this.request(retry_ids, page || 0, function() {
498                                    methods.sidebar.render(methods.sidebar.get_type());
499                                });
500                            },
501
502                            // find and load the next un-cached results
503                            load_more: function(id) {
504                                $.nplModal('log', 'theme sidebars.comments.load_more()', {
505                                    id: id
506                                });
507
508                                if (methods.nplModal.is_nextgen_gallery()
509                                    &&  $.nplModal('get_setting', 'enable_routing', false)
510                                    &&  $.nplModal('get_setting', 'enable_comments', false)) {
511                                    var precache_ids = [];
512
513                                    var direction = methods.sidebars.datacache.get_carousel_navigation_direction();
514                                    var count = 3;
515
516                                    // Retrieve X images in the same direction the user is navigating
517                                    for (var i = 0; i <= count; i++) {
518                                        if (direction === 'left') {
519                                            var uncached_id = this.get_prev_uncached_image_id(id, i);
520                                        } else {
521                                            var uncached_id = this.get_next_uncached_image_id(id, i);
522                                        }
523
524                                        // Push the recently found IDs to this._cache so they can't be included again
525                                        if (!this._cache[uncached_id]) {
526                                            this._cache[uncached_id] = 'Placeholder stub';
527                                            precache_ids.push(uncached_id);
528                                        }
529                                    }
530
531                                    precache_ids = this.sort_uniq(precache_ids);
532
533                                    if (precache_ids.length !== 0) {
534                                        this.expanded_request(precache_ids);
535                                    }
536                                }
537                            },
538
539                            sort_uniq: function(ids) {
540                                return ids.sort().filter(function(item, pos, ary) {
541                                    return !pos || item !== ary[pos - 1];
542                                })
543                            },
544
545                            get_cache_by_id: function(id) {
546                                return this._cache[id];
547                            }
548                        },
549                        comments: {
550                            // called after render(), initialize logic & events
551                            init: function() {
552                                $.nplModal('log', 'theme sidebars.comments.init()');
553
554                                if ($('#nggpl-comments-wrapper').length !== 1) { return; }
555
556                                // jquery's .val() fails with hidden fields
557                                var origin_url = document.getElementById("ngg_comment_origin_url");
558                                if (typeof origin_url !== 'undefined' && origin_url !== null) {
559                                    origin_url.value = window.location.href.toString();
560                                }
561
562                                // It is much faster to change the target attribute globally here than through WP hooks
563                                $('#npl_sidebar').find('a').each(function() {
564                                    if ($(this).attr('id') === 'nggpl-comment-logout') {
565                                        $(this).attr('href', $(this).attr('href') + '?redirect_to=' + window.location.toString());
566                                    } else {
567                                        $(this).attr('target', '_blank');
568                                    }
569                                });
570
571                                $('#nggpl-respond-form').on('submit', function (event) {
572                                    event.preventDefault();
573                                    var commentstatus = $('#nggpl-comment-status');
574                                    $('#npl_wrapper').addClass('npl-sidebar-overlay-open');
575                                    $.ajax({
576                                        type: $(this).attr('method'),
577                                        url: $(this).attr('action'),
578                                        data: $(this).serialize(),
579                                        dataType: 'json',
580                                        success: function (data) {
581                                            if (data.success === true) {
582                                                $('#nggpl-comment').val('');
583                                                $('#nggpl-comments-title').val('');
584                                                var image_id = methods.galleria.get_current_image_id();
585                                                methods.sidebars.datacache.expanded_request(image_id, function() {
586                                                    methods.sidebar.render(methods.sidebars.comments.get_type(), image_id);
587                                                });
588                                            } else {
589                                                commentstatus.addClass('error')
590                                                    .html(data);
591                                                $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
592                                            }
593                                        },
594                                        complete: function (jqXHR, status) {
595                                        },
596                                        error: function (jqXHR) {
597                                            commentstatus.addClass('error').html(jqXHR.responseText);
598                                            $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
599                                        }
600                                    });
601                                });
602
603                                $("#npl_sidebar .nggpl-button, #nggpl-comment-form-wrapper input[type='submit']").each(function() {
604                                    var $this = $(this);
605                                    $this.css({
606                                        'color': $.nplModal('get_setting', 'sidebar_button_color'),
607                                        'background-color': $.nplModal('get_setting', 'sidebar_button_background')
608                                    });
609                                });
610
611                                // handles 'Reply' links
612                                $('.nggpl-reply-to-comment').on('click', function(event) {
613                                    event.preventDefault();
614                                    // all that wordpress needs is the comment_parent value
615                                    $('#nggpl-comment_parent').val($(this).data('comment-id'));
616                                    $('#nggpl-comment-reply-status').removeClass('hidden');
617
618                                    // IE has issues setting focus on invisible elements. Be wary
619                                    $('#nggpl-commentform').find(':input').filter(':visible').first().trigger('focus');
620                                    $('#npl_sidebar').scrollTop($('#nggpl-comments-bottom').offset().top);
621                                });
622
623                                // handles "cancel reply" link
624                                $('#nggpl-comment-reply-status a').on('click', function(event) {
625                                    event.preventDefault();
626                                    $('#nggpl-comment_parent').val('0');
627                                    $('#nggpl-comment-reply-status').addClass('hidden');
628                                });
629
630                                // handles comment AJAX pagination
631                                $('#nggpl-comment-nav-below a').on('click', function(event) {
632                                    event.preventDefault();
633                                    $('#npl_wrapper').addClass('npl-sidebar-overlay-open');
634                                    var page_id = $(this).data('page-id');
635                                    methods.sidebars.datacache.request(
636                                        [methods.galleria.get_current_image_id()],
637                                        page_id,
638                                        function() {
639                                            methods.sidebar.render('comments', methods.galleria.get_current_image_id());
640                                        }
641                                    );
642                                });
643
644                                self.trigger('npl_sidebar_rendered');
645
646                                if (methods.nplModal.is_nextgen_gallery()
647                                    &&  $.nplModal('get_setting', 'enable_routing', false)
648                                    &&  $.nplModal('get_setting', 'enable_sharing', false)) {
649                                    $('#nggpl-comments-image-share-icons').removeClass('disabled');
650                                    methods.share_icons.create(
651                                        '#nggpl-comments-image-share-icons',
652                                        methods.icons.get_overlay_color()
653                                    );
654                                }
655
656                                methods.sidebars.comments.wait_for_image_to_reveal();
657                            },
658
659                            wait_for_image_to_reveal: function() {
660                                if (!methods.sidebars.comments.events.between_loadstart_and_finish) {
661                                    $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
662                                } else {
663                                    methods.sidebars.comments.events.waiting_for_image_to_load = true;
664                                }
665                            },
666
667                            // returns the display area content from cache
668                            render: function(id) {
669                                $.nplModal('log', 'theme sidebars.comments.render()', {
670                                    id: id
671                                });
672
673                                var datacache = methods.sidebars.datacache;
674                                var cache = datacache.get_cache_by_id(id);
675
676                                // the server has already answered for this image and had nothing; do not
677                                // ask again on our own -- Retry forces a fresh request
678                                if (cache === 'No data') {
679                                    datacache.show_failed([id], 0);
680                                    return;
681                                }
682
683                                if (!cache) {
684                                    $('#npl_wrapper').addClass('npl-sidebar-overlay-open');
685                                    datacache.expanded_request(id, function() {
686                                        cache = datacache.get_cache_by_id(id);
687                                        // The request succeeded but returned nothing for this image, so
688                                        // there is nothing to render: fail visibly rather than throw on
689                                        // cache.comments and leave the overlay up (#211).
690                                        if (!datacache.is_renderable(cache)) {
691                                            datacache.show_failed([id], 0);
692                                            return;
693                                        }
694                                        $('#npl_sidebar').html(cache.comments['rendered_view']);
695                                        methods.sidebars.comments.init();
696                                    });
697                                } else if (cache === 'Placeholder stub') {
698                                    $('#npl_wrapper').addClass('npl-sidebar-overlay-open');
699                                    var interval = setInterval(function() {
700                                        cache = datacache.get_cache_by_id(id);
701                                        if (cache === 'Placeholder stub') { return; }
702                                        clearInterval(interval);
703                                        // the request came back without this image
704                                        if (cache === 'No data') {
705                                            datacache.show_failed([id], 0);
706                                            return;
707                                        }
708                                        // the stub is gone: its request failed, so ask again in the foreground
709                                        if (!cache) {
710                                            methods.sidebar.render(methods.sidebar.get_type());
711                                            return;
712                                        }
713                                        $('#npl_sidebar').html(cache.comments['rendered_view']);
714                                        methods.sidebars.comments.init();
715                                    }, 250);
716                                } else {
717                                    $('#npl_sidebar').html(cache.comments['rendered_view']);
718                                    $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
719                                }
720                            },
721
722                            get_type: function() {
723                                return 'comments';
724                            },
725
726                            events: {
727                                waiting_for_image_to_load: false,
728                                between_loadstart_and_finish: false,
729
730                                bind: function() {
731                                    $.nplModal('log', 'theme sidebars.comments.events.bind()');
732
733                                    if (methods.nplModal.is_nextgen_gallery()
734                                    &&  $.nplModal('get_setting', 'enable_routing', false)
735                                    &&  $.nplModal('get_setting', 'enable_comments', false)
736                                    &&  !methods.nplModal.is_random_source()) {
737                                        self.bind('loadstart', this.loadstart);
738                                        self.bind('npl_init', this.npl_init);
739                                        self.bind('npl_init_keys', this.npl_init_keys);
740                                        self.bind('image', this.image);
741                                    }
742                                },
743
744                                npl_init: function() {
745                                    $.nplModal('log', 'theme sidebars.comments.events.npl_init()');
746
747                                    // Adds comment toolbar button
748                                    var comment_button = $('<i/>')
749                                        .addClass('nggpl-toolbar-button-comment fas fa-comment')
750                                        .attr({'title': $.nplModal('get_setting', 'i18n').toggle_social_sidebar});
751                                    methods.thumbnails.register_button(
752                                        comment_button,
753                                        function(event) {
754                                            methods.sidebar.toggle(methods.sidebars.comments.get_type());
755                                            event.preventDefault();
756                                        }
757                                    );
758                                },
759
760                                image: function() {
761                                    methods.sidebars.comments.events.between_loadstart_and_finish = false;
762
763                                    if (methods.sidebars.comments.events.waiting_for_image_to_load) {
764                                        methods.sidebars.comments.events.waiting_for_image_to_load = false;
765                                        $('#npl_wrapper').removeClass('npl-sidebar-overlay-open');
766                                    }
767                                },
768
769                                _loadstart_run_once: false,
770                                loadstart: function(e) {
771                                    $.nplModal('log', 'theme sidebars.comments.events.npl_init_complete()');
772
773                                    methods.sidebars.comments.events.between_loadstart_and_finish = true;
774
775                                    if (methods.nplModal.is_nextgen_gallery() && !methods.sidebars.comments._loadstart_run_once) {
776                                        // possibly display the comments sidebar at startup
777                                        if (($.nplModal('get_state').sidebar && $.nplModal('get_state').sidebar === methods.sidebars.comments.get_type())
778                                        ||  $.nplModal('get_setting', 'display_comments')) {
779                                            methods.sidebar.open(methods.sidebars.comments.get_type());
780                                        }
781                                        methods.sidebars.comments._loadstart_run_once = true;
782                                    } else if (methods.nplModal.is_nextgen_gallery()
783                                           &&  $.nplModal('get_setting', 'enable_routing', false)
784                                           &&  $.nplModal('get_setting', 'enable_comments', false)) {
785                                            // updates the sidebar
786                                            methods.sidebars.datacache.load_more(methods.galleria.get_current_image_id());
787                                            if (methods.sidebar.is_open() && methods.sidebar.get_type() === methods.sidebars.comments.get_type()) {
788                                                methods.sidebar.render(methods.sidebars.comments.get_type());
789                                            }
790                                    }
791                                },
792
793                                npl_init_keys: function() {
794                                    $.nplModal('log', 'theme sidebars.comments.events.npl_init_keys()');
795                                    var input_types = methods.galleria.get_keybinding_exclude_list();
796                                    if (methods.nplModal.is_nextgen_gallery()
797                                    &&  $.nplModal('get_setting', 'enable_routing', false)
798                                    &&  $.nplModal('get_setting', 'enable_comments', false))
799                                    {
800                                        self.attachKeyboard({
801                                            // spacebar
802                                            32: function () {
803                                                if (!$(document.activeElement).is(input_types)) {
804                                                    methods.sidebar.toggle(methods.sidebars.comments.get_type());
805                                                }
806                                            }
807                                        });
808                                    }
809                                }
810                            }
811                        }
812                    },
813                    thumbnails: {
814                        is_open: function() {
815                            return !$('#npl_wrapper').hasClass('npl-carousel-closed');
816                        },
817                        toggle: function() {
818                            $.nplModal('log', 'theme thumbnails.toggle()');
819
820                            if (this.is_open()) {
821                                this.close();
822                            } else {
823                                this.open();
824                            }
825                        },
826                        open: function() {
827                            $.nplModal('log', 'theme thumbnails.open()');
828
829                            $('#npl_wrapper').removeClass('npl-carousel-closed');
830                            $('.galleria-dock-toggle-container i').toggleClass('fa-angle-up fa-angle-down');
831                            $('.galleria-dock-toggle-container').find('[data-fa-i2svg]').toggleClass('fa-angle-up fa-angle-down');
832                            $(window).trigger('resize');
833                        },
834                        close: function() {
835                            $.nplModal('log', 'theme thumbnails.close()');
836
837                            $('#npl_wrapper').addClass('npl-carousel-closed');
838                            $('.galleria-dock-toggle-container i').toggleClass('fa-angle-up fa-angle-down');
839                            $('.galleria-dock-toggle-container').find('[data-fa-i2svg]').toggleClass('fa-angle-up fa-angle-down');
840                            $(window).trigger('resize');
841                        },
842                        adjust_container: function() {
843                            $.nplModal('log', 'theme thumbnails.adjust_container()');
844                            // Important: this must be delayed for a short while after Galleria.resize() is invoked to allow
845                            // Galleria to finish repositioning itself and prevent possible race scenario.
846                            setTimeout(function() {
847                                var available_width = self.$('thumbnails-container').width()
848                                    - self.$('nextgen-buttons').width()
849                                    - self.$('thumb-nav-left').width()
850                                    - self.$('thumb-nav-right').width();
851                                if (available_width <= (70 * 4)) {
852                                    self.$('container').addClass('nggpl-carousel-too-small');
853                                } else {
854                                    self.$('container').removeClass('nggpl-carousel-too-small');
855                                }
856                            }, 250);
857                        },
858                        _buttons: [],
859                        register_button: function(button, callback) {
860                            $.nplModal('log', 'theme thumbnails.register_button()', {
861                                button: button
862                            });
863
864                            var wrapper = $('<span class="nggpl-button nggpl-toolbar-button"/>');
865                            if ($.nplModal('get_setting', 'icon_background_enabled', false)
866                            &&  $.nplModal('get_setting', 'icon_background_rounded', false)) {
867                                wrapper.addClass('nggpl-rounded');
868                            }
869                            wrapper.html(button);
870                            this._buttons.push(wrapper);
871                            $(self._dom.stage).append(wrapper);
872
873                            wrapper.on('click', function(event) {
874                                callback(event)
875                            });
876                        },
877                        get_registered_buttons: function() {
878                            $.nplModal('log', 'theme thumbnails.get_registered_buttons', {
879                                buttons: this._buttons
880                            });
881                            return this._buttons;
882                        },
883                        events: {
884                            bind: function() {
885                                $.nplModal('log', 'theme thumbnails.events.bind()');
886
887                                self.bind('loadfinish', methods.thumbnails.adjust_container);
888                                self.bind('loadfinish', this.loadfinish);
889                                self.bind('npl_sidebar_opened', methods.thumbnails.adjust_container);
890                                self.bind('npl_sidebar_closed', methods.thumbnails.adjust_container);
891                                self.bind('npl_init', this.npl_init);
892                                self.bind('npl_init_complete', this.npl_init_complete);
893
894                                // This prevents multiple handlers from running simultaneously, and multiple
895                                // events pile up when listening to 'resize' - only the last handler is invoked
896                                // if 250ms pass after the last triggering.
897                                methods.galleria.bind_once('resize', methods.thumbnails.adjust_container, 250);
898                            },
899                            npl_init: function() {
900                                $.nplModal('log', 'theme thumbnails.events.npl_init()');
901
902                                if ('numbers' === options.thumbnails) {
903                                    self.$('container').addClass('nggpl-carousel-numbers');
904                                    self.$('thumbnails-list').css('color', methods.icons.get_color());
905
906                                    // Because numeric entries have a different width than at startup we
907                                    // must manually invoke updateCarousel() here so that we don't start
908                                    // with the carousel in the wrong position
909                                    self.updateCarousel();
910
911                                } else if ('lazy' === options.thumbnails) {
912                                    // load all thumbnails, 10 at a time, until all have been retrieved
913                                    self.lazyLoadChunks(10);
914                                }
915
916                                self.$('thumbnails-container').css({background: $.nplModal('get_setting', 'carousel_background_color')});
917
918                                // create carousel next/prev links
919                                var next_thumbs_button = $('<i/>')
920                                    .addClass('fas fa-angle-right')
921                                    .css({color: methods.icons.get_color()});
922                                var prev_thumbs_button = $('<i/>')
923                                    .addClass('fas fa-angle-left')
924                                    .css({color: methods.icons.get_color()});
925                                $(self._dom.stage).append(next_thumbs_button);
926                                $(self._dom.stage).append(prev_thumbs_button);
927                                self.append({'thumb-nav-left': prev_thumbs_button});
928                                self.append({'thumb-nav-right': next_thumbs_button});
929
930                                // Create thumbnails-container toggle button
931                                self.addElement('dock-toggle-container');
932                                var dock_toggle_container = self.$('dock-toggle-container')
933                                    .css({background: $.nplModal('get_setting', 'carousel_background_color')});
934                                var dock_toggle_button = $('<i/>').addClass('fas fa-angle-down')
935                                    .css({color: $.nplModal('get_setting', 'carousel_text_color')});
936
937                                // the container was marked hidden by the parent wrapper, adjust the angle-icon accordingly
938                                if (!methods.thumbnails.is_open()) {
939                                    dock_toggle_button.toggleClass('fa-angle-up fa-angle-down');
940                                }
941
942                                $(self._dom.stage).append(dock_toggle_button);
943                                self.append({'dock-toggle-container': dock_toggle_button});
944                                dock_toggle_container.on('click', self.proxy(function() {
945                                    methods.thumbnails.toggle();
946                                }));
947
948                                // Add playback controls
949                                var play_button = $('<i/>')
950                                    .addClass('nggpl-toolbar-button-play fas fa-play')
951                                    .attr({'title': $.nplModal('get_setting', 'i18n').play_pause});
952                                if (this._playing) {
953                                    play_button.removeClass('fa-play').addClass('fa-pause');
954                                }
955                                methods.thumbnails.register_button(
956                                    play_button,
957                                    function(event) {
958                                        event.preventDefault();
959                                        self.playToggle();
960                                        var $target = $(event.currentTarget);
961                                        $target.find('i').toggleClass('fa-play fa-pause');
962                                        $target.find('[data-fa-i2svg]').toggleClass('fa-play fa-pause');
963                                    }
964                                );
965
966                                // add info controls; handles animation of both the info & dock-toggle-container divs
967                                var info_button = $('<i/>')
968                                    .addClass('nggpl-toolbar-button-info fas fa-circle-info')
969                                    .attr({'title': $.nplModal('get_setting', 'i18n').toggle_image_info});
970                                methods.thumbnails.register_button(
971                                    info_button,
972                                    function(event) {
973                                        event.preventDefault();
974                                        methods.info.toggle();
975                                    }
976                                );
977                            },
978                            npl_init_complete: function() {
979                                $.nplModal('log', 'theme thumbnails.events.npl_init_complete()');
980
981                                var display_buttons = methods.thumbnails.get_registered_buttons();
982                                // assign all of our buttons a (possibly custom) color
983                                for (i = 0; i <= (display_buttons.length - 1); i++) {
984                                    display_buttons[i].css({
985                                        'color': methods.icons.get_color(),
986                                        'background-color': methods.icons.get_background()
987                                    });
988                                }
989                                self.addElement('nextgen-buttons');
990                                self.append({'nextgen-buttons': display_buttons});
991                                self.prependChild('thumbnails-container', 'nextgen-buttons');
992                                self.prependChild('info', 'dock-toggle-container');
993
994                                if (!$.nplModal('mobile.browser.any')) {
995                                    self.addIdleState(self.get('dock-toggle-button'), {opacity: 0});
996                                }
997                            },
998                            // Hide the parent lightbox's spinner
999                            _loadfinish_ran_once: false,
1000                            loadfinish: function() {
1001                                $.nplModal('log', 'theme thumbnails.events.loadfinish()');
1002
1003                                if (methods.thumbnails.events._loadfinish_ran_once) {
1004                                    return;
1005                                }
1006
1007                                $('#npl_spinner_container').addClass('hidden');
1008                                methods.thumbnails.events._loadfinish_ran_once = true;
1009                            }
1010                        }
1011                    },
1012                    info: {
1013                        is_open: function() {
1014                            return $('#npl_wrapper').hasClass('npl-info-open');
1015                        },
1016
1017                        toggle: function() {
1018                            $.nplModal('log', 'theme info.toggle()');
1019
1020                            if (this.is_open()) {
1021                                this.close();
1022                            } else {
1023                                this.open();
1024                            }
1025                        },
1026
1027                        open: function() {
1028                            $.nplModal('log', 'theme info.open()');
1029
1030                            $('#npl_wrapper').addClass('npl-info-open');
1031                        },
1032
1033                        close: function() {
1034                            $.nplModal('log', 'theme info.close()');
1035
1036                            $('#npl_wrapper').removeClass('npl-info-open');
1037                        },
1038
1039                        events: {
1040                            bind: function() {
1041                                $.nplModal('log', 'theme info.events.bind()');
1042
1043                                self.bind('npl_init', this.npl_init);
1044                                self.bind('loadfinish', this.loadfinish);
1045                            },
1046
1047                            npl_init: function() {
1048                                $.nplModal('log', 'theme info.events.npl_init()');
1049
1050                                var needsnewparent = self.$('info').detach();
1051                                self.$('thumbnails-container').append(needsnewparent);
1052
1053                                // Add social share icons to the infobar. ID is important, sidebars could add their own icons
1054                                self.prependChild(
1055                                    'info-text',
1056                                    $('<div/>')
1057                                        .attr('id', 'galleria-image-share-icons')
1058                                        .attr('class', 'galleria-image-share-icons')
1059                                );
1060                                if (carousel_text_color = $.nplModal('get_setting', 'carousel_text_color')) {
1061                                    self.$('info-title').css({color: carousel_text_color});
1062                                    self.$('info-description').css({color: carousel_text_color});
1063                                }
1064                                self.$('info, info-text, info-title, info-description').css({background: $.nplModal('get_setting', 'carousel_background_color')});
1065                            },
1066
1067                            _loadfinish_ran_once: false,
1068                            loadfinish: function() {
1069                                $.nplModal('log', 'theme info.events.loadfinish()');
1070                                // anchors in our image captions / descriptions must have target=_blank
1071                                self.$('info-title, info-description').find('a').each(function() {
1072                                    $(this).attr('target', '_blank');
1073                                    $(this).css('color', methods.icons.get_color());
1074                                });
1075
1076                                methods.info.events._loadfinish_ran_once = true;
1077                            }
1078                        }
1079                    },
1080                    icons: {
1081                        get_color: function() {
1082                            return $.nplModal('get_setting', 'icon_color');
1083                        },
1084                        get_background: function() {
1085                            var iconcolor = $.nplModal('get_setting', 'carousel_background_color');
1086                            if ($.nplModal('get_setting', 'icon_background_enabled', false)) {
1087                                iconcolor = $.nplModal('get_setting', 'icon_background');
1088                            }
1089                            return iconcolor;
1090                        },
1091                        get_overlay_color: function() {
1092                            return $.nplModal('get_setting', 'overlay_icon_color');
1093                        }
1094                    },
1095                    nplModal: {
1096                        close: function() {
1097                            $.nplModal('close_modal');
1098                        },
1099                        is_random_source: function() {
1100                            var gallery = $.nplModal('get_gallery_from_id', $.nplModal('get_state').gallery_id);
1101                            var result = ($.inArray(gallery.source, ['random', 'random_images']) !== -1);
1102
1103                            $.nplModal('log', 'theme nplModal.is_random_source()', {
1104                                result: result
1105                            });
1106
1107                            return result;
1108                        },
1109                        is_nextgen_gallery: function() {
1110                            var retval = $.nplModal('get_state').gallery_id !== '!';
1111
1112                            $.nplModal('log', 'theme nplModal.is_nextgen_gallery()', {
1113                                result: retval
1114                            });
1115
1116                            return retval;
1117                        },
1118                        is_nextgen_widget: function() {
1119                            var retval = false;
1120                            var gallery = $.nplModal('get_gallery_from_id', $.nplModal('get_state').gallery_id);
1121                            var slug = gallery.slug;
1122                            if (slug) {
1123                                retval = slug.indexOf('widget-ngg-images') !== -1;
1124                            }
1125
1126                            $.nplModal('log', 'theme nplModal.is_nextgen_widget()', {
1127                                result: retval
1128                            });
1129
1130                            return retval;
1131                        },
1132                        events: {
1133                            bind: function() {
1134                                $.nplModal('log', 'theme nplModal.bind()');
1135
1136                                if (Galleria.IPAD || Galleria.IPHONE) {
1137                                    self.$('container').addClass('nggpl-ios-browser');
1138                                }
1139
1140                                // update the parent url when a new image has been chosen or the slideshow advances
1141                                if (methods.nplModal.is_nextgen_gallery()) {
1142                                    self.bind('image', this.image);
1143                                }
1144
1145                                // handle updates to the current url once opened; most likely due to the back/forward button
1146                                if (methods.nplModal.is_nextgen_gallery()
1147                                &&  $.nplModal('get_setting', 'enable_routing', false)
1148                                &&  $.nplModal('get_setting', 'enable_comments', false)) {
1149                                    $('#npl_content').on('npl_url_handler', this.unhandled_url_change);
1150                                }
1151
1152                                if ($.nplModal('get_setting', 'protect_images', false)) {
1153                                    $('.galleria-image').on('dragstart', function(event) {
1154                                        event.preventDefault();
1155                                    });
1156                                    self.bind('npl_init', this.npl_init);
1157                                    self.bind('npl_init_complete', this.npl_init_complete);
1158                                }
1159
1160                                jQuery('#npl_content').on('npl_closing', this.closing);
1161                            },
1162                            npl_init: function() {
1163                                $.nplModal('log', 'theme nplModal.events.npl_init()');
1164
1165                                self.addElement('image-protection');
1166                                document.oncontextmenu = function(event) {
1167                                    event = event || window.event;
1168                                    event.preventDefault();
1169                                };
1170                            },
1171                            npl_init_complete: function() {
1172                                $.nplModal('log', 'theme nplModal.events.npl_init_complete()');
1173                                self.prependChild('images', 'image-protection');
1174                            },
1175                            _image_ran_once: false,
1176                            image: function() {
1177                                $.nplModal('log', 'theme nplModal.events.image()');
1178                                if (methods.nplModal.events._image_ran_once) {
1179                                    if (!methods.nplModal.is_random_source()) {
1180                                        var image_id = self.getData(self.getIndex()).image_id;
1181                                        var sidebar_string = methods.sidebar.is_open() ? '/' + methods.sidebar.get_type() : '';
1182                                        var state = $.nplModal('get_state');
1183                                        state.image_id = image_id;
1184                                        $.nplModal('set_state', state);
1185                                        if ($.nplModal('get_setting', 'enable_routing', false)) {
1186                                            $.nplModal(
1187                                                'router.navigate',
1188                                                $.nplModal('get_setting', 'router_slug') + '/' + $.nplModal('get_state').slug + '/' + image_id + sidebar_string,
1189                                                true
1190                                            );
1191                                        }
1192                                    }
1193                                }
1194                                methods.nplModal.events._image_ran_once = true;
1195                            },
1196                            unhandled_url_change: function(event, state) {
1197                                $.nplModal('log', 'theme nplModal.unhandled_url_change()', {
1198                                    event: event,
1199                                    state: state
1200                                });
1201                                for (var i = 0; i <= (self.getDataLength() - 1); i++) {
1202                                    if (state.image_id === self.getData(i).image_id) {
1203                                        self.show(i);
1204                                    }
1205                                }
1206                                if (methods.sidebar.is_open() && state.sidebar == null) {
1207                                    methods.sidebar.close();
1208                                } else if (!methods.sidebar.is_open()
1209                                       &&  typeof state.sidebar !== 'undefined'
1210                                       &&  state.sidebar !== null) {
1211                                    methods.sidebar.open(state.sidebar);
1212                                }
1213                            },
1214                            closing: function() {
1215                                // without this our bound keys will continue to activate even aften
1216                                // Galleria.destroy() has been called and the container div emptied
1217                                self.detachKeyboard();
1218                            }
1219                        }
1220                    },
1221                    galleria: {
1222                        bind_once: function(event, callback, timeout) {
1223                            var timer_id = undefined;
1224                            window.addEventListener(event, function() {
1225                                if (typeof timer_id === 'undefined') {
1226                                    clearTimeout(timer_id);
1227                                    timer_id = undefined;
1228                                }
1229                                timer_id = setTimeout(function() {
1230                                    timer_id = undefined;
1231                                    callback();
1232                                }, timeout);
1233                            });
1234                        },
1235                        get_displayed_gallery_setting: function(name, def) {
1236                            var tmp = def;
1237                            var gallery = $.nplModal('get_gallery_from_id', $.nplModal('get_state').gallery_id);
1238                            if (gallery && gallery.display_settings) {
1239                                if (typeof gallery.display_settings[name] !== 'undefined') {
1240                                    tmp = gallery.display_settings[name];
1241                                }
1242                                var original = gallery.display_settings.original_settings;
1243                                if (original && typeof original[name] !== 'undefined') {
1244                                    tmp = original[name];
1245                                }
1246                            }
1247                            if (tmp === '1') tmp = true;
1248                            if (tmp === '0') tmp = false;
1249                            if (tmp === 1) tmp = true;
1250                            if (tmp === 0) tmp = false;
1251
1252                            $.nplModal('log', 'theme galleria.get_displayed_gallery_setting()', {
1253                                name: name,
1254                                result: tmp
1255                            });
1256
1257                            return tmp;
1258                        },
1259                        get_keybinding_exclude_list: function() {
1260                            return 'textarea, input';
1261                        },
1262                        get_current_image_id: function() {
1263                            return self.getData(self.getIndex()).image_id;
1264                        },
1265                        // returns the Galleria image-index based on the provided image id
1266                        get_index_from_id: function(id) {
1267                            var retval = null;
1268                            for (var i = 0; i <= (self.getDataLength() - 1); i++) {
1269                                if (id === self.getData(i).image_id) {
1270                                    retval = i;
1271                                }
1272                            }
1273                            return retval;
1274                        },
1275                        events: {
1276                            bind: function() {
1277                                $.nplModal('log', 'theme galleria.events.bind()');
1278                                self.bind('touchmove', this.touchmove);
1279                                self.bind('npl_init', this.npl_init);
1280                                self.bind('npl_init_keys', this.npl_init_keys);
1281                                self.bind('npl_init_complete', this.npl_init_complete);
1282                                self.bind('loadstart', this.loadstart);
1283                                self.bind('loadfinish', this.loadfinish);
1284                                $(window).on('resize orientationchange', this.browserchanged);
1285                            },
1286                            browserchanged: function() {
1287                                $.nplModal('log', 'theme galleria.events.browserchanged()');
1288                                setTimeout(function() {
1289                                    self.rescale();
1290                                }, 200);
1291                            },
1292                            npl_init: function() {
1293                                $.nplModal('log', 'theme galleria.events.npl_init()');
1294                                // for some reason this isn't an option that can be passed at startup
1295                                self.setPlaytime(($.nplModal('get_setting', 'slideshow_speed', 5) * 1000));
1296
1297                                // Create next / back links
1298                                var next_image_button = $('<i/>')
1299                                    .addClass('fas fa-angle-right')
1300                                    .css({color: methods.icons.get_overlay_color()});
1301                                var prev_image_button = $('<i/>')
1302                                    .addClass('fas fa-angle-left')
1303                                    .css({color: methods.icons.get_overlay_color()});
1304                                $(self._dom.stage).append(next_image_button);
1305                                $(self._dom.stage).append(prev_image_button);
1306                                self.append({'image-nav-left': prev_image_button});
1307                                self.append({'image-nav-right': next_image_button});
1308
1309                                self.$('counter').css({color: methods.icons.get_overlay_color()});
1310                            },
1311                            npl_init_keys: function() {
1312                                $.nplModal('log', 'theme galleria.events.npl_init_keys()');
1313                                var input_types = methods.galleria.get_keybinding_exclude_list();
1314                                self.attachKeyboard({
1315                                    left: function() {
1316                                        if (!$(document.activeElement).is(input_types)) {
1317                                            this.prev();
1318                                        }
1319                                    },
1320                                    right: function() {
1321                                        if (!$(document.activeElement).is(input_types)) {
1322                                            this.next();
1323                                        }
1324                                    },
1325                                    down: function() {
1326                                        if (!$(document.activeElement).is(input_types)) {
1327                                            methods.thumbnails.toggle();
1328                                        }
1329                                    },
1330                                    up: function() {
1331                                        if (!$(document.activeElement).is(input_types)) {
1332                                            methods.info.toggle();
1333                                        }
1334                                    },
1335                                    // escape key
1336                                    27: function(event) {
1337                                        event.preventDefault();
1338                                        methods.nplModal.close();
1339                                    }
1340                                });
1341                            },
1342                            npl_init_complete: function() {
1343                                $.nplModal('log', 'theme galleria.events.npl_init_complete()');
1344                                if (!$.nplModal('mobile.browser.any')) {
1345                                    self.addIdleState(self.get('counter'),            {opacity: 0});
1346                                    self.addIdleState(self.get('image-nav-left'),     {opacity: 0});
1347                                    self.addIdleState(self.get('image-nav-right'),    {opacity: 0});
1348                                }
1349                            },
1350                            touchmove: function(event) {
1351                                // prevent scrolling on elements without the 'scrollable' class
1352                                if (!$('.scrollable').has($(event.target)).length) {
1353                                    $.nplModal('log', 'theme galleria.events.touchmove() prevented scrolling');
1354                                    event.preventDefault();
1355                                }
1356                            },
1357                            loadstart: function(event) {
1358                                $.nplModal('log', 'theme galleria.events.loadstart()', {
1359                                    event: event
1360                                });
1361
1362                                if (!event.cached) {
1363                                    var button = $('#npl_button_close').find('i');
1364                                    button.removeClass('fa-times-circle');
1365                                }
1366                            },
1367                            _loadfinish_ran_once: false,
1368                            loadfinish: function() {
1369                                $.nplModal('log', 'theme galleria.events.loadfinish()');
1370
1371                                var button = $('#npl_button_close').find('i');
1372                                button.addClass('fa-times');
1373
1374                                // Re-center the first image once the stage has settled; skip a
1375                                // zero-width stage so touch swipe positioning is not re-anchored.
1376                                if (methods.galleria.events._loadfinish_ran_once) {
1377                                    return;
1378                                }
1379                                methods.galleria.events._loadfinish_ran_once = true;
1380                                setTimeout(function() {
1381                                    if (self.$('stage').width() > 0) {
1382                                        self.rescale();
1383                                    }
1384                                }, 200);
1385                            }
1386                        }
1387                    },
1388                    share_icons: {
1389                        // No-op stub — replaced by npl-social-sharing.js via npl_ready event
1390                        // when enable_sharing is true. If sharing is disabled, these stubs
1391                        // ensure any caller (e.g. sidebars.comments.init) fails gracefully.
1392                        strip_html:    function() { return ''; },
1393                        create:        function() {},
1394                        get_share_url: function() { return ''; },
1395                        events: {
1396                            bind: function() {}
1397                        }
1398                    },
1399                    tiktok: {
1400                        cleanup: function() {
1401                            $('#npl_content .ngg-tiktok-container').remove();
1402                            $('#npl_content .ngg-tiktok-error').remove();
1403                            $('#npl_wrapper').removeClass('npl-tiktok-mode');
1404                        },
1405
1406                        handle_content: function(data) {
1407                            this.cleanup();
1408                            if (!data.tiktok_play_url && !data.tiktok_share_url) return;
1409
1410                            // Check if NextGEN_TikTok helper is available
1411                            if (typeof NextGEN_TikTok === 'undefined' || !NextGEN_TikTok.handle_content) {
1412                                console.warn('NextGEN_TikTok helper not available.');
1413                                return;
1414                            }
1415
1416                            $('#npl_wrapper').addClass('npl-tiktok-mode');
1417
1418                            var $img = $('#npl_content .galleria-image img:visible');
1419
1420                            NextGEN_TikTok.handle_content({
1421                                playUrl: data.tiktok_play_url,
1422                                shareUrl: data.tiktok_share_url,
1423                                container: $('#npl_content .galleria-stage'),
1424                                width: $img.width(),
1425                                height: $img.height(),
1426                            });
1427                        },
1428
1429                        events: {
1430                            bind: function() {
1431                                self.bind('loadstart', function() {
1432                                    methods.tiktok.cleanup();
1433                                });
1434                                self.bind('loadfinish', function(e) {
1435                                    var data = e.data || self.getData(self.getIndex());
1436                                    // Check if this is a TikTok video
1437                                    var isTikTok = (data.is_tiktok == 1 || data.is_tiktok == '1' || data.is_tiktok === true)
1438                                               || (data.tiktok_play_url || data.tiktok_share_url);
1439                                    if (isTikTok) {
1440                                        methods.tiktok.handle_content(data);
1441                                    }
1442                                });
1443                            }
1444                        }
1445                    },
1446                    video: {
1447                        cleanup: function() {
1448                            $('#npl_content .ngg-video-container').remove();
1449                            $('#npl_content .ngg-video-error').remove();
1450                            $('#npl_wrapper').removeClass('npl-video-mode');
1451                        },
1452
1453                        getVideoEmbedInfo: function(url, autoplay) {
1454                            if (!url || typeof url !== 'string') {
1455                                return { isEmbed: false, embedUrl: null, useIframe: false };
1456                            }
1457
1458                            var embedUrl = null;
1459                            var useIframe = false;
1460                            var isEmbed = false;
1461                            var autoplayParam = autoplay ? '1' : '0';
1462
1463                            var youtubeRegex = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/;
1464                            var youtubeMatch = url.match(youtubeRegex);
1465                            if (youtubeMatch) {
1466                                embedUrl = 'https://www.youtube.com/embed/' + youtubeMatch[1] + '?autoplay=' + autoplayParam + '&rel=0';
1467                                useIframe = true;
1468                                isEmbed = true;
1469                            }
1470
1471                            var vimeoRegex = /(?:vimeo\.com\/)(?:.*\/)?(\d+)/;
1472                            var vimeoMatch = url.match(vimeoRegex);
1473                            if (vimeoMatch && !isEmbed) {
1474                                embedUrl = 'https://player.vimeo.com/video/' + vimeoMatch[1] + '?autoplay=' + autoplayParam;
1475                                useIframe = true;
1476                                isEmbed = true;
1477                            }
1478
1479                            var dailymotionRegex = /(?:dailymotion\.com\/(?:embed\/)?video\/|dai\.ly\/)([a-zA-Z0-9]+)/;
1480                            var dailymotionMatch = url.match(dailymotionRegex);
1481                            if (dailymotionMatch && !isEmbed) {
1482                                embedUrl = 'https://www.dailymotion.com/embed/video/' + dailymotionMatch[1] + '?autoplay=' + autoplayParam;
1483                                useIframe = true;
1484                                isEmbed = true;
1485                            }
1486
1487                            var twitchRegex = /(?:twitch\.tv\/videos\/|twitch\.tv\/)(\d+|[a-zA-Z0-9_]+)/;
1488                            var twitchMatch = url.match(twitchRegex);
1489                            if (twitchMatch && !isEmbed) {
1490                                var twitchId = twitchMatch[1];
1491                                var autoplayStr = autoplay ? 'true' : 'false';
1492                                if (/^\d+$/.test(twitchId)) {
1493                                    embedUrl = 'https://player.twitch.tv/?video=' + twitchI
1493d + '&autoplay=' + autoplayStr + '&parent=' + window.location.hostname;
1494                                } else {
1495                                    embedUrl = 'https://player.twitch.tv/?channel=' + twitchId + '&autoplay=' + autoplayStr + '&parent=' + window.location.hostname;
1496                                }
1497                                useIframe = true;
1498                                isEmbed = true;
1499                            }
1500
1501                            var videopressRegex = /(?:videopress\.com\/v\/|video\.wordpress\.com\/v\/)([a-zA-Z0-9]+)/;
1502                            var videopressMatch = url.match(videopressRegex);
1503                            if (videopressMatch && !isEmbed) {
1504                                embedUrl = 'https://videopress.com/embed/' + videopressMatch[1] + '?autoplay=' + autoplayParam;
1505                                useIframe = true;
1506                                isEmbed = true;
1507                            }
1508
1509                            var wistiaRegex = /(?:wistia\.com\/medias\/|wistia\.net\/embed\/)([a-zA-Z0-9]+)/;
1510                            var wistiaMatch = url.match(wistiaRegex);
1511                            if (wistiaMatch && !isEmbed) {
1512                                embedUrl = 'https://fast.wistia.net/embed/iframe/' + wistiaMatch[1] + '?autoplay=' + autoplayParam;
1513                                useIframe = true;
1514                                isEmbed = true;
1515                            }
1516
1517                            return {
1518                                isEmbed: isEmbed,
1519                                embedUrl: embedUrl,
1520                                useIframe: useIframe,
1521                                originalUrl: url
1522                            };
1523                        },
1524
1525                        handle_content: function(data) {
1526                            this.cleanup();
1527                            if (!data.video_link) return;
1528
1529                            $('#npl_wrapper').addClass('npl-video-mode');
1530
1531                            // Get video control properties from data
1532                            var isAutoplay = (data.is_video_autoplay == 1 || data.is_video_autoplay == '1' || data.is_video_autoplay === true);
1533                            var showControls = (data.show_video_controls == 1 || data.show_video_controls == '1' || data.show_video_controls === true);
1534                            var showPlayPause = (data.show_play_pause_controls == 1 || data.show_play_pause_controls == '1' || data.show_play_pause_controls === true);
1535
1536                            // Default to true if not specified
1537                            if (typeof data.is_video_autoplay === 'undefined') {
1538                                isAutoplay = true;
1539                            }
1540                            if (typeof data.show_video_controls === 'undefined') {
1541                                showControls = true;
1542                            }
1543
1544                            var $container = $('#npl_content .galleria-stage');
1545                            var $videoContainer = $('<div class="ngg-video-container"></div>');
1546
1547                            var videoInfo = this.getVideoEmbedInfo(data.video_link, isAutoplay);
1548                            var $videoElement;
1549
1550                            if (videoInfo.useIframe && videoInfo.embedUrl) {
1551                                // For iframe embeds, controls are handled by the platform
1552                                $videoElement = $('<iframe>', {
1553                                    src: videoInfo.embedUrl,
1554                                    frameborder: '0',
1555                                    allow: 'autoplay; fullscreen; picture-in-picture',
1556                                    allowfullscreen: true,
1557                                    webkitallowfullscreen: true,
1558                                    mozallowfullscreen: true
1559                                });
1560
1561                                if (!showControls && videoInfo.embedUrl.indexOf('youtube.com') !== -1) {
1562                                    videoInfo.embedUrl += (videoInfo.embedUrl.indexOf('?') === -1 ? '?' : '&') + 'controls=0';
1563                                    $videoElement.attr('src', videoInfo.embedUrl);
1564                                }
1565                            } else {
1566                                var videoAttrs = {
1567                                    playsinline: true,
1568                                    preload: 'auto'
1569                                };
1570
1571                                if (showControls) {
1572                                    videoAttrs.controls = true;
1573                                }
1574
1575                                if (isAutoplay) {
1576                                    videoAttrs.autoplay = true;
1577                                    videoAttrs.muted = true; // Required for autoplay in modern browsers
1578                                }
1579
1580                                $videoElement = $('<video>', videoAttrs);
1581                                $videoElement.attr('src', data.video_link);
1582
1583                                if (showPlayPause && !showControls) {
1584                                    var $playPauseBtn = $('<button>', {
1585                                        class: 'ngg-video-play-pause-btn',
1586                                        type: 'button',
1587                                        html: '<i class="fas fa-play"></i>'
1588                                    });
1589
1590                                    $playPauseBtn.on('click', function() {
1591                                        var video = $videoElement[0];
1592                                        if (video.paused) {
1593                                            video.play();
1594                                            $playPauseBtn.html('<i class="fas fa-pause"></i>');
1595                                        } else {
1596                                            video.pause();
1597                                            $playPauseBtn.html('<i class="fas fa-play"></i>');
1598                                        }
1599                                    });
1600
1601                                    $videoElement.on('play', function() {
1602                                        $playPauseBtn.html('<i class="fas fa-pause"></i>');
1603                                    });
1604
1605                                    $videoElement.on('pause', function() {
1606                                        $playPauseBtn.html('<i class="fas fa-play"></i>');
1607                                    });
1608
1609                                    $videoContainer.append($playPauseBtn);
1610                                }
1611
1612                                if (isAutoplay) {
1613                                    $videoElement.on('playing', function() {
1614                                        try {
1615                                            $videoElement.prop('muted', false);
1616                                        } catch (e) {
1617                                            console.debug('Could not unmute video automatically:', e);
1618                                        }
1619                                    });
1620
1621                                    setTimeout(function() {
1622                                        if ($videoElement[0] && $videoElement[0].paused) {
1623                                            $videoElement[0].play().catch(function(err) {
1624                                                console.warn('Video auto
1624play failed:', err);
1625                                            });
1626                                        }
1627                                    }, 100);
1628                                }
1629                            }
1630
1631
1632														// Size iframe to match lightbox image sizing behavior
1633														// Use responsive aspect ratio (16:9 default for most platforms)
1634														const aspectRatio = 16 / 9;
1635														const maxWidth = window.innerWidth * 0.9;
1636														const maxHeight = window.innerHeight * 0.9;
1637														let width = Math.min(maxWidth, 1080);
1638														let height = width / aspectRatio;
1639
1640														if (height > maxHeight) {
1641															height = maxHeight;
1642															width = height * aspectRatio;
1643														}
1644
1645														$videoContainer.css({
1646															width: width + "px",
1647															height: height + "px",
1648															maxWidth: "100%",
1649															maxHeight: "90vh"
1650														});
1651
1652                            $videoContainer.append($videoElement);
1653                            $container.append($videoContainer);
1654                        },
1655
1656                        events: {
1657                            bind: function() {
1658                                self.bind('loadstart', function() {
1659                                    methods.video.cleanup();
1660                                });
1661                                self.bind('loadfinish', function(e) {
1662                                    var data = e.data || self.getData(self.getIndex());
1663                                    // Check if this is a generic video (not TikTok)
1664                                    var isTikTok = (data.is_tiktok == 1 || data.is_tiktok == '1' || data.is_tiktok === true)
1665                                                || (data.tiktok_play_url || data.tiktok_share_url);
1666                                    var hasVideoLink = (data.is_video_link == 1 || data.is_video_link == '1' || data.is_video_link === true)
1667                                                   || (data.video_link && data.video_link.length > 0);
1668                                    if (!isTikTok && hasVideoLink) {
1669                                        methods.video.handle_content(data);
1670                                    }
1671                                });
1672                            }
1673                        }
1674                    }
1675                };
1676
1677                $.nplModal('log', 'theme initialization - about to bind events');
1678
1679                // Load our modules
1680                methods.galleria.events.bind();
1681                methods.nplModal.events.bind();
1682                methods.thumbnails.events.bind();
1683                methods.info.events.bind();
1684                methods.share_icons.events.bind();
1685                methods.sidebar.events.bind();
1686                methods.sidebars.datacache.events.bind();
1687                methods.sidebars.comments.events.bind();
1688                methods.tiktok.events.bind();
1689                methods.video.events.bind();
1690
1691                $.nplModal('log', 'theme initialization - done binding events');
1692
1693                $.nplModal('log', 'theme initialization - about to trigger npl_ready');
1694                $(self._target).trigger('npl_ready', {galleria_theme: self, methods: methods});
1695
1696                $.nplModal('log', 'theme initialization - about to trigger npl_init');
1697                self.trigger('npl_init');
1698
1699                $.nplModal('log', 'theme initialization - about to trigger npl_init_keys');
1700                self.trigger('npl_init_keys');
1701
1702                $.nplModal('log', 'theme initialization - about to trigger npl_init_complete');
1703                self.trigger('npl_init_complete');
1704            }
1705        });
1706    };
1707
1708    // In case this theme is enqueued before Galleria.js is loaded we
1709    // will periodically check if Galleria exists before calling
1710    // Galleria.addTheme()
1711    var interval = setInterval(function() {
1712        if (typeof window.Galleria !== 'undefined') {
1713            clearInterval(interval);
1714            add_nggpl_theme();
1715        }
1716    }, 10);
1717
1718}(jQuery));

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.