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.