1(function($) { 2 3 'use strict'; 4 5 function NMWW_Plugin() { 6 this.init(); 7 }; 8 9 NMWW_Plugin.prototype = { 10 11 /** 12 * Init 13 */ 14 init: function() { 15 var self = this; 16 17 self.$document = $(document); 18 self.$body = $('body'); 19 self.$documentBody = $(document.body); 20 self.$panelWrapper = $('#nmww-panel-items'); 21 self.$pageItemsTable = $('#nmww-items-table'); 22 23 self.isLoggedIn = (self.$body.hasClass('logged-in')) ? true : false; 24 25 self.wishlistItems = []; 26 27 self.cookieName = 'nmww-items'; 28 self.cookieExpires = parseInt(nmww_data.cookie_expires); 29 30 // WPML: Add language code prefix to cookie name when WPML is used 31 if (nmww_data.addLocaleSuffix != '0') { 32 var locale = $('html').attr('lang').toLowerCase(); 33 locale = locale.replace(/-/g, '_'); // Replace "-" with "_" so language code matches with "get_locale" PHP function 34 self.cookieName = self.cookieName + '-' + locale; 35 } 36 37 //self.itemsUpdateAjax = false; 38 39 self.timestampsInterval = null; 40 41 self.setUp(); 42 43 /* Bind: Buttons (add/remove items) */ 44 self.$document.on('click', '.nmww-button', function(e) { 45 e.preventDefault(); 46 47 // Make sure wishlist data is done loading 48 if (! self.$body.hasClass('nmww-buttons-ready')) { 49 return; 50 } 51 52 // Is login required? 53 if ('0' != nmww_data.login_require && ! self.$body.hasClass('logged-in')) { 54 self.$body.trigger('nmww_login_required'); 55 window.location.replace(nmww_data.login_url+'?nmw_login_required'); 56 return; 57 } 58 59 // Is an Ajax request running? 60 /*if (self.itemsUpdateAjax) { 61 self.itemsUpdateAjax.abort(); // Abort Ajax request 62 self.itemsUpdateAjax = false; 63 }*/ 64 65 var $button = $(this); 66 67 if (! $button.hasClass('loading')) { 68 var itemId = $button.data('product-id'); 69 70 // Make sure a product ID exists 71 if (typeof itemId == 'undefined') { 72 return false; 73 } 74 75 self.buttonSetState(itemId, 'loading'); 76 77 self.buttonToggle($button, itemId); 78 } 79 }); 80 81 // Is the Wishlist panel included? 82 if (self.$panelWrapper.length) { 83 /* Bind: Wishlist panel - Button (header menu etc.) */ 84 self.$document.on('click', '.nmww-wishlist-button', function(e) { 85 e.preventDefault(); 86 if (! self.$body.hasClass('nmww-panel-open')) { 87 self.panelOpen(); 88 } else { 89 self.panelClose(); 90 } 91 }); 92 93 /* Bind: Wishlist panel - Close button */ 94 $('#nmww-panel-close').on('click', function(e) { 95 e.preventDefault(); 96 self.panelClose(); 97 }); 98 99 /* Bind: Wishlist panel - Add to cart events */ 100 self.$documentBody.on('adding_to_cart', function(e, $thisbutton, data) { 101 var $wishlistItem = $thisbutton.closest('.nmww-panel-item'); 102 if ($wishlistItem.length) { 103 self.panelItemSetState($wishlistItem, 'loading'); 104 } 105 }); 106 self.$documentBody.on('added_to_cart', function(e, fragments, cart_hash, $thisbutton) { 107 self.$panelWrapper.children('.loading').removeClass('loading'); 108 }); 109 110 /* Bind: Wishlist panel - Remove item buttons */ 111 self.$document.on('click', '.nmww-panel-item-remove', function(e) { 112 e.preventDefault(); 113 self.panelItemOnRemoveButton(this); 114 }); 115 116 /* Bind: Wishlist panel - Prevent cart panel from showing after adding to cart */ 117 self.$documentBody.on('added_to_cart', function() { 118 if (self.$body.hasClass('nmww-panel-open')) { 119 setTimeout(function() { 120 // Make sure the wishlist panel hasn't been closed during the timeout 121 if (self.$body.hasClass('nmww-panel-open')) { 122 var $cartPanelCloseButton = $('.wc-block-mini-cart__drawer').find('.wc-block-components-drawer__close'); 123 if ($cartPanelCloseButton.length) { 124 // Trigger click on cart panel close button 125 $cartPanelCloseButton[0].dispatchEvent(new Event('click', {bubbles: true})); 126 } 127 } 128 }, 200); 129 } 130 }); 131 132 /* Bind: Wishlist panel - Overlay */ 133 $('#nmww-panel-overlay').on('click', function() { 134 self.panelClose(); 135 }); 136 137 /* 138 * Bind: Wishlist panel - Item buttons - Prevent AJAX add-to-cart on Checkout page (page needs refresh to show added product) 139 * 140 * Note: The "should_send_ajax_request.adding_to_cart" event is triggered from the "../plugins/woocommerce/assets/js/frontend/add-to-cart.js" file 141 */ 142 self.$documentBody.on('should_send_ajax_request.adding_to_cart', function(e, $thisbutton) { 143 if (self.$body.hasClass('nmww-panel-open') && self.$body.hasClass('woocommerce-checkout')) { 144 window.location.href = $thisbutton.attr('href'); 145 return false; 146 } 147 }); 148 } 149 150 // Is this the Wishlist page? 151 if (self.$pageItemsTable.length) { 152 /* Bind: Page - Item - Remove button */ 153 $('#nmww-items-table').on('click', '.nmww-item-remove-button', function(e) { 154 e.preventDefault(); 155 self.pageRemoveItem(this); 156 }); 157 158 /* Bind: Page - Item - Adding to cart event */ 159 self.$documentBody.on('adding_to_cart', function(e, $thisbutton, data) { 160 $thisbutton.closest('.nmww-item').addClass('adding'); 161 }); 162 163 /* Bind: Page - Item - Added to cart event */ 164 self.$documentBody.on('added_to_cart', function(e, fragments, cartHash, $thisbutton) { 165 $thisbutton.closest('.nmww-item').removeClass('adding'); 166 }); 167 } 168 169 /* Bind: Share - Button click */ 170 $('#nmww-share').on('click', '.nmww-share-button', function() { 171 self.shareSocialButtonClick(this); 172 }); 173 }, 174 175 /** 176 * Set up: Init 177 */ 178 setUp: function() { 179 var self = this; 180 181 // Get cookie items 182 if (undefined !== Cookies.getJSON) { // Note: Cookies.getJSON was removed from "Cookies" library in a recent version 183 var cookieItems = Cookies.getJSON(self.cookieName); 184 } else { 185 var cookieItems = Cookies.get(self.cookieName); 186 // Parse to JSON, if possible 187 try { 188 cookieItems = JSON.parse(cookieItems); 189 } catch (error) { 190 //console.error('Essential Wishlist - Unable to JSON parse cookie items: '+error); 191 } 192 } 193
194 // Wishlist button (menus): Sett cookie count early to avoid delay 195 if (cookieItems && cookieItems.length) { 196 self.wishlistButtonUpdate(cookieItems.length); 197 } 198 199 // Is this the Wishlist page? 200 if (self.$pageItemsTable.length) { 201 var $pageItemsTableTbody = self.$pageItemsTable.children('tbody'), 202 itemsCount = $pageItemsTableTbody.children().length; 203 204 if (itemsCount > 0) { 205 // Get and set wishlist items 206 var items = self.$pageItemsTable.data('items'), 207 currentUrl = new URL(window.location.href), 208 setCookie = (currentUrl.searchParams.has('nmwshare')) ? false : true; // Don't set cookie when share URL is used 209 210 self.itemsSet(items, setCookie); // Args: items, set cookie 211 212 self.timestampUpdateAll($pageItemsTableTbody, 'long'); 213 // Update item timestamps once every minute 214 self.timestampsInterval = setInterval(function() { 215 self.timestampUpdateAll($pageItemsTableTbody, 'long'); 216 }, 60000); // 60 seconds 217 } 218 219 self.wishlistButtonUpdate(itemsCount); 220 221 // No need to get data on Wishlist page 222 return; 223 } 224 225 var ajaxLoadData = true, 226 data = { 227 nonce: nmww_data.nonce 228 }; 229 230 if (self.isLoggedIn) { 231 // Get items via AJAX when logged-in 232 data['get_items'] = 1; 233 } else { 234 // Does the Wishlist cookie exist? 235 if (! cookieItems || cookieItems.length == 0) { 236 self.itemsSet([], true); // Args: items, set cookie 237 238 // Buttons: Include and set states 239 self.singleProductAddButton(); 240 self.buttonsSetState(); 241 self.wishlistButtonUpdate(); 242 self.shareUrlUpdate(); 243 244 self.panelSetState('empty'); 245 246 // No need to send an AJAX request if user is logged-out and no items are added 247 ajaxLoadData = false; 248 } else { 249 self.itemsSet(cookieItems, false); // Args: items, set cookie 250 } 251 } 252 253 if (ajaxLoadData) { 254 var ajaxUrl = woocommerce_params.wc_ajax_url.toString().replace('%%endpoint%%', 'nmww_get_data'); 255 256 // Only get panel items when panel is enabled 257 if (self.$panelWrapper.length) { 258 data['get_panel_items'] = 1; 259 } 260 261 $.ajax({
262 type: 'POST', 263 url: ajaxUrl, 264 data: data, 265 dataType: 'json', 266 cache: false, 267 headers: {'cache-control': 'no-cache'}, 268 error: function(XMLHttpRequest, textStatus, errorThrown) { 269 console.log('NMWW: AJAX error - setUp() - ' + errorThrown); 270 }, 271 success: function(json) { 272 if (self.isLoggedIn) { 273 // Set wishlist items 274 if (json.hasOwnProperty('items') && json.items.length > 0) { 275 self.itemsSet(json.items, true); // Args: items, set cookie 276 } 277 } 278 279 // Buttons: Include and set states 280 self.singleProductAddButton(); 281 self.buttonsSetState(); 282 self.wishlistButtonUpdate(); 283 self.shareUrlUpdate(); 284 285 // Panel: Include content and set state 286 if (json.hasOwnProperty('panel_items') && Object.keys(json.panel_items).length > 0) { 287 self.panelCountUpdate(json.count.number, json.count.text); 288 self.panelAddItems(json.panel_items); 289 self.panelSetState('has-items'); 290 } else { 291 self.panelSetState('empty'); 292 } 293 } 294 }); 295 } 296 }, 297 298 /** 299 * Items: Set 300 */ 301 itemsSet: function(items, setCookie) { 302 var self = this; 303 304 self.wishlistItems = items; // Note: There's no need to set this when removing/updating (since "self.wishlistItems" has already been updated), but it makes no difference.. 305 306 if (setCookie) { 307 Cookies.set(self.cookieName, JSON.stringify(items), { expires: self.cookieExpires }); 308 } 309 }, 310 311 /** 312 * Items - Array: Check if items exists 313 * 314 * Based on https://stackoverflow.com/a/21689286 315 */ 316 itemsArrayItemExists: function(itemId) { 317 var self = this, 318 existingItem = self.wishlistItems.findIndex(existingItem => existingItem.id === itemId); 319 return existingItem; 320 }, 321 322 323 /** 324 * Items - Array: Add item 325 */ 326 itemsArrayAdd: function(item) { 327 var self = this; 328 self.wishlistItems.unshift(item); // Prepend product-id to array (display newly added products first) 329 }, 330 331 332 /** 333 * Items - Array: Remove item 334 */ 335 itemsArrayRemove: function(existingItem) { 336 var self = this; 337 self.wishlistItems.splice(existingItem, 1); 338 }, 339 340 /** 341 * Items: Update 342 */ 343 itemsUpdate: function(item, isAdding) { 344 var self = this; 345 346 self.itemsSet(self.wishlistItems, true); // Args: items, set cookie 347 348 var data = { 349 action: 'nmww_update_items', 350 nonce: nmww_data.nonce, 351 is_adding: (isAdding) ? 1 : 0, 352 item_added: (isAdding) ? JSON.stringify(item) : 0, 353 }; 354 355 if (self.isLoggedIn) { 356 // Convert IDs array to string 357 data['items'] = JSON.stringify(self.wishlistItems); 358 } 359 360 // AJAX: Update items in user-meta (if logged in) and add panel item 361 //self.itemsUpdateAjax = $.ajax({ 362 $.ajax({
363 type: 'POST', 364 url: woocommerce_params.ajax_url, 365 data: data, 366 dataType: 'json', 367 cache: false, 368 headers: {'cache-control': 'no-cache'}, 369 /*complete: function() { 370 self.itemsUpdateAjax = false; 371 },*/ 372 error: function(XMLHttpRequest, textStatus, errorThrown) { 373 console.log('NMWW: AJAX error - itemsUpdate() - ' + errorThrown); 374 375 self.buttonSetState(item.id, 'loaded'); 376 }, 377 success: function(json) { 378 // Panel: Update count 379 if (json.hasOwnProperty('count')) { 380 self.panelCountUpdate(json.count.number, json.count.text); 381 } 382 383 // Panel - Adding: Prepend new item 384 if (json.hasOwnProperty('item_added')) { 385 self.panelAddItem(json.item_added); 386 } 387 388 // Panel - Removing: Remove item 389 if (! isAdding) { 390 self.panelRemoveItem(item.id); 391 } 392 393 self.buttonSetState(item.id, 'loaded'); 394 } 395 }); 396 }, 397 398 /** 399 * Wishlist button(s): Update menu button(s) (set class and count) 400 */ 401 wishlistButtonUpdate: function(count) { 402 // Make sure menu button count is enabled 403 if ('0' == nmww_data.show_menu_count) { 404 return; 405 } 406 407 var self = this, 408 $wishlistButtons = $('.nmww-wishlist-button'); 409 410 if ($wishlistButtons.length) { 411 var $wishlistButton, 412 $wishlistButtonCount, 413 $wishlistButtonAnchor, 414 wishlistButtonCountElem, 415 count = (count) ? count : self.wishlistItems.length; 416 417 $wishlistButtons.each(function() { 418 $wishlistButton = $(this); 419 420 // Add count element, or set its count if already added 421 $wishlistButtonCount = $wishlistButton.find('.nmww-wishlist-button-count'); 422 if ($wishlistButtonCount.length) { 423 $wishlistButtonCount.text(count); 424 } else { 425 // Append count element to child "a" tag, or button container if no tag is found 426 $wishlistButtonAnchor = $wishlistButton.children('a'); 427 wishlistButtonCountElem = '<span class="nmww-wishlist-button-count">'+count+'</span>'; 428 if ($wishlistButtonAnchor.length) { 429 $wishlistButtonAnchor.append(wishlistButtonCountElem); 430 } else { 431 $wishlistButton.append(wishlistButtonCountElem); 432 } 433 } 434 435 // Toggle "has items" class for button element 436 if (count > 0) { 437 $wishlistButton.addClass('has-items'); 438 } else { 439 $wishlistButton.removeClass('has-items'); 440 } 441 }); 442 } 443 }, 444 445 /** 446 * Buttons (sitewide): Add button classes for added items (classes must be set with JS when page-caching is enabled) 447 */ 448 buttonsSetState: function() { 449 var self = this; 450 451 for (var i = 0; i < self.wishlistItems.length; i++) { 452 $('.nmww-item-' + self.wishlistItems[i].id + '-button').attr('title', nmww_data.button_title_remove).addClass('added'); 453 } 454 455 // Add class to show buttons 456 self.$body.addClass('nmww-buttons-ready'); 457 458 // All Products block: Set buttons states 459 var $allProductsBlock = $('.wc-block-all-products').first(); 460 if ($allProductsBlock.length) { 461 // Bind MutationObserver for main block container 462 var allProductsBlockObserver = new MutationObserver((mutationList, observer) => { 463 // Does the block contain any "loader" elements? If not, block should be done loading 464 if (! $allProductsBlock.find('.is-loading').length) { 465 // Set button states 466 for (var i = 0; i < self.wishlistItems.length; i++) { 467 $allProductsBlock.find('.nmww-item-' + self.wishlistItems[i].id + '-button').attr('title', nmww_data.button_title_remove).addClass('added'); 468 } 469 } 470 }); 471 allProductsBlockObserver.observe($allProductsBlock[0], {attributes: false, childList: true, subtree: true}); 472 } 473 }, 474 475 /** 476 * Button (single): Set state (change button class and title) 477 */ 478 buttonSetState: function(itemId, state) { 479 var self = this, 480 $buttons = $('.nmww-item-' + itemId + '-button'); // Get all wishlist buttons with the same product-id 481 482 switch (state) { 483 case 'loading': 484 $buttons.addClass('loading'); 485 break; 486 case 'adding': 487 $buttons.addClass('added'); 488 $buttons.attr('title', nmww_data.button_title_remove); 489 break; 490 case 'removing': 491 $buttons.removeClass('added'); 492 $buttons.attr('title', nmww_data.button_title_add); 493 break; 494 case 'loaded': 495 $buttons.removeClass('loading'); 496 break; 497 } 498 }, 499 500 /** 501 * Button (single): Add/remove item 502 */ 503 buttonToggle: function($button, itemId) { 504 var self = this, 505 item = { 506 id: itemId, 507 timestamp: Date.now() 508 }, 509 isAdding = true; 510 511 // Does the product-id exist in "self.wishlistItems"? 512 var existingItem = self.itemsArrayItemExists(itemId); 513 514 if (-1 == existingItem) { 515 self.itemsArrayAdd(item); 516 517 self.panelSetState('loading-has-items'); 518 } else { 519 isAdding = false; 520 521 self.itemsArrayRemove(existingItem); 522 523 self.panelItemSetState(self.$panelWrapper.children('.nmww-panel-item-'+itemId), 'removing'); 524 } 525 526 self.itemsUpdate(item, isAdding); 527 528 self.wishlistButtonUpdate(); 529 self.shareUrlUpdate(); 530 531 var buttonState = (isAdding) ? 'adding' : 'removing'; 532 self.buttonSetState(itemId, buttonState); 533 534 // Open panel (if enabled) 535 if (isAdding && '0' != nmww_data.panel_open_when_adding) { 536 setTimeout(function() { 537 self.panelOpen(); 538 }, 250); 539 } 540 }, 541 542 /** 543 * Single product: Add gallery button 544 */ 545 singleProductAddButton: function() { 546 var self = this; 547 548 if (nmww_data.single_product_gallery_button !== '0' && self.$body.hasClass('single-product')) { 549 var productId = nmww_data.single_product_id; 550 551 if (productId) { 552 // Note: "Active" state is set separately with self.buttonsSetState() function
553 var button = '<a class="nmww-button nmww-item-' + productId + '-button for-single-product-gallery" data-product-id="' + productId + '" title="' + nmww_data.button_title_add + '"><i class="' + nmww_data.button_icon_class + '"></i></a>'; 554 555 $('.woocommerce-product-gallery').prepend(button); 556 } 557 } 558 }, 559 560 /** 561 * Panel: Open 562 */ 563 panelOpen: function() { 564 var self = this; 565 566 // Set scrollbar width so an offset can be set via CSS when opening panel 567 document.documentElement.style.setProperty('--nmww--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + 'px'); 568 569 self.$body.addClass('nmww-panel-overlay-show'); 570 self.$body.addClass('nmww-panel-open'); 571 572 // Update item timestamps once every minute 573 self.timestampsInterval = setInterval(function() { 574 self.timestampUpdateAll(self.$panelWrapper); 575 }, 60000); // 60 seconds 576 577 self.timestampUpdateAll(self.$panelWrapper); 578 }, 579 580 /** 581 * Panel: Close 582 */ 583 panelClose: function() { 584 var self = this; 585 586 // Stop item timestamp updating 587 clearInterval(self.timestampsInterval); 588 589 self.$body.removeClass('nmww-panel-open'); 590 591 self.$body.addClass('nmww-panel-closing'); // Start closing (fade out overlay) 592 setTimeout(function() { 593 self.$body.removeClass('nmww-panel-overlay-show nmww-panel-closing'); // Hide 594 595 // Add to cart buttons: Remove "added" class 596 //self.$panelWrapper.children().find('a.added').removeClass('added'); 597 }, 400); 598 }, 599 600 /** 601 * Panel: Set state 602 */ 603 panelSetState: function(state) { 604 var states = { 605 'has-items': 'has-items', 606 'empty': 'is-empty', 607 'loading': 'is-loading', 608 'loading-has-items': 'has-items is-loading' 609 }, 610 panelClass = states[state]; 611 612 $('#nmww-panel').removeClass('is-loading is-empty has-items').addClass(panelClass); 613 }, 614 615 /** 616 * Panel: Set ready class 617 */ 618 panelSetReadyClass: function() { 619 var self = this; 620 self.$body.addClass('nmww-panel-ready'); 621 }, 622 623 /** 624 * Panel: Add multiple items 625 */ 626 panelAddItems: function(items) { 627 var self = this; 628 629 // Append items 630 for (var item in items) { 631 if (items.hasOwnProperty(item)) { 632 self.$panelWrapper.append(items[item]); 633 } 634 } 635 636 // Update timestamps 637 self.timestampUpdateAll(self.$panelWrapper); 638 }, 639 640 /** 641 * Panel: Add single item 642 */ 643 panelAddItem: function(item) { 644 var self = this; 645 646 // Prepend single item 647 self.$panelWrapper.prepend(item); 648 649 self.panelSetState('has-items'); 650 651 // Set timestamp 652 var $item = self.$panelWrapper.children().first(); 653 self.timestampUpdate($item); 654 }, 655 656 /** 657 * Panel: Remove single item 658 */ 659 panelRemoveItem: function(itemId) { 660 var self = this; 661 self.$panelWrapper.children('.nmww-panel-item-' + itemId).remove(); 662 663 // Show "empty" container if not items are added 664 if (! self.$panelWrapper.children().length) { 665 self.panelSetState('empty'); 666 } 667 }, 668 669 /** 670 * Panel: Item - Remove button action 671 */ 672 panelItemOnRemoveButton: function(button) { 673 var self = this, 674 $button = $(button), 675 $itemWrapper = $button.closest('.nmww-panel-item'), 676 itemId = $itemWrapper.data('product-id'), 677 item = { 678 id: itemId 679 }; 680 681 if ($itemWrapper.hasClass('removing')) { 682 return; 683 } 684 685 $itemWrapper.addClass('removing'); 686 687 // Does the product-id exist in "self.wishlistItems"? 688 var existingItem = self.itemsArrayItemExists(itemId); 689 if (-1 !== existingItem) { 690 self.itemsArrayRemove(existingItem); 691 } 692 self.itemsUpdate(item, false); // Args: item, isAdding 693 694 self.wishlistButtonUpdate(); 695 self.shareUrlUpdate(); 696 697 self.buttonSetState(itemId, 'removing'); 698 }, 699 700 /** 701 * Panel: Item - Set state 702 */ 703 panelItemSetState: function($item, state) { 704 var self = this, 705 states = { 706 loading: 'loading', 707 removing: 'removing' 708 }, 709 itemClass = states[state]; 710 711 $item.addClass(itemClass); 712 }, 713 714 /** 715 * Panel: Count - Update 716 */ 717 panelCountUpdate: function(count, countText) { 718 var text = (count > 0) ? '(' + countText + ')' : ''; 719 $('#nmww-panel-item-count').text(text); 720 }, 721 722 /** 723 * Page: Remove item 724 */ 725 pageRemoveItem: function(button) { 726 // Show "loader" overlay 727 $('#nmww-overlay').addClass('show'); 728 729 var self = this, 730 $itemTr = $(button).closest('tr'), 731 itemId = $itemTr.data('product-id'), 732 item = { 733 id: itemId 734 }; 735 736 $itemTr.addClass('removing'); 737 738 // Does the product-id exist in "self.wishlistItems"? 739 var existingItem = self.itemsArrayItemExists(itemId); 740 if (-1 !== existingItem) { 741 self.itemsArrayRemove(existingItem); 742 } 743 self.itemsUpdate(item, false); // Args: item, isAdding 744 745 setTimeout(function() { 746 $itemTr.remove(); 747 748 self.wishlistButtonUpdate(); 749 self.shareUrlUpdate(); 750 751 // Show "wishlist empty" container? 752 if (self.$pageItemsTable.children('tbody').children().length == 0) { 753 self.$pageItemsTable.closest('.nmww-items').addClass('hide'); 754 $('#nmww-empty').addClass('show'); 755 } 756 757 // Hide "loader" overlay 758 $('#nmww-overlay').removeClass('show'); 759 }, 500); 760 }, 761 762 /** 763 * Timestamp: Update for all items 764 */ 765 timestampUpdateAll: function($itemsWrapper, style) { 766 var self = this; 767 $itemsWrapper.children().each(function() { 768 self.timestampUpdate($(this), style); 769 }); 770 }, 771 772 /** 773 * Timestamp: Update for single item - Based on: https://stackoverflow.com/a/69122877 774 */ 775 timestampUpdate: function($item, style) { 776 var $itemTimestampElem = $item.find('.nmww-item-timestamp'), 777 itemTimestamp = $itemTimestampElem.data('timestamp'), 778 secondsElapsed = (itemTimestamp - Date.now()) / 1000, 779 locale = $('html').attr('lang'), 780 style = (style) ? style : 'long', // Alt default: '
780short' 781 rtf = new Intl.RelativeTimeFormat(locale, {numeric: 'auto', style: style}), 782 units = { 783 years: 3600 * 24 * 365, 784 months: 3600 * 24 * 30, 785 weeks: 3600 * 24 * 7, 786 days: 3600 * 24, 787 hours: 3600, 788 minutes: 60, 789 seconds: 1 790 }, 791 delta, 792 timeFormatted = ''; 793 794 // Has less than a minute passed? 795 if (Math.abs(secondsElapsed) < 60) { 796 // Return "now" 797 timeFormatted = rtf.format(0, 'seconds'); 798 } else { 799 for (var unit in units) { 800 if (units[unit] < Math.abs(secondsElapsed)) { 801 delta = secondsElapsed / units[unit]; 802 timeFormatted = rtf.format(Math.round(delta), unit); 803 break; 804 } 805 } 806 } 807 808 $itemTimestampElem.text(timeFormatted); 809 }, 810 811 /** 812 * Share: Social button/icon click 813 */ 814 shareSocialButtonClick: function(button) { 815 var $button = $(button), 816 href = $button.data('href'), 817 shareUrl = $('#nmww-share').attr('data-share-url'), 818 target = $button.attr('target'); 819 820 // Add Wishlist URL to share URL 821 href = href.toString().replace('%%wishlist_url%%', shareUrl); 822 823 window.open(href, target); 824 }, 825 826 /** 827 * Share: Update URL 828 */ 829 shareUrlUpdate: function() { 830 var self = this, 831 itemIds = []; 832 833 // No need to update if Wishlist is empty 834 if (self.wishlistItems.length == 0) { 835 return; 836 } 837 838 for (var i = 0; i < self.wishlistItems.length; i++) { 839 itemIds.push(self.wishlistItems[i].id); 840 } 841 itemIds = itemIds.join(','); // Convert IDs array into comma separated string 842 843 var shareUrl = nmww_data.share_base_url+encodeURIComponent(itemIds); 844 845 $('#nmww-share').attr('data-share-url', shareUrl); 846 }, 847 848 /** 849 * Share: Copy URL 850 */ 851 /*shareUrlCopy: function(copyButton) { 852 var shareInput = document.getElementById('nmww-panel-share-url-input'); 853 854 // Wait until "has-copied" class has been removed 855 if (copyButton.classList.contains('has-copied')) { 856 return; 857 } 858 859 shareInput.select(); // Select text field 860 shareInput.setSelectionRange(0, 99999); // For mobile devices 861 862 // Copy to clipboard 863 navigator.clipboard.writeText(shareInput.value); 864 865 // Add/remove "copied" class 866 copyButton.classList.add('has-copied'); 867 setTimeout(function() { 868 copyButton.classList.remove('has-copied'); 869 }, 2000); 870 },*/ 871 } 872 873 $(function() { // Doc ready 874 // Initialize script 875 $.nmwwPlugin = new NMWW_Plugin(); 876 }); 877 878})(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.