1var isBusiness = false; 2try { 3 if (document.currentScript && document.currentScript.src) { 4 const urlParams = new URL(document.currentScript.src).searchParams; 5 isBusiness = urlParams.get('business') === 'true'; 6 } 7} catch (e) {} 8 9if (!isBusiness && (window.location.pathname.includes('/dev/') || window.location.pathname.includes('/business/') || window.location.pathname.includes('/admin/'))) { 10 isBusiness = true; 11} 12 13var endpoint = (isBusiness ? '/dev/api/business/favorite.php' : '/api/favorite.php'); 14var isMobile = /Android.+Mobile|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); 15var favoriteProducts = []; 16let added = false; 17timeout = false; 18timeout2 = false; 19$(document).ready(function ($) { 20 var webPath = $('#webpath').val() || ''; 21 endpoint = webPath + (isBusiness ? '/dev/api/business/favorite.php' : '/api/favorite.php'); 22 23 $('body').on('keyup', '#favoriteSearch', function() { 24 var value = $(this).val().toLowerCase(); 25 26 $('#favoritePopup form .col-12').each(function() { 27 var col = $(this); 28 if (col.find('input[value="new"]').length > 0 || 29 col.find('button[type="submit"]').length > 0 || 30 col.find('#favoriteSearch').length > 0) { 31 return; 32 } 33 34 var text = col.text().toLowerCase(); 35 var isMatch = text.indexOf(value) > -1; 36 37 col.toggle(isMatch); 38 39 col.next('hr').toggle(isMatch); 40 }); 41 }); 42 43 44 $('body').on('click', '.favorite', function (e) { 45 e.preventDefault(); 46 47 var thisBtn = $(this); 48 var productId = thisBtn.data('product'); 49 var start = thisBtn.data('date'); 50 51 if(isBusiness) { 52 var listHashContainer = thisBtn.closest('div[data-listhash]'); 53 var isRemovingFromList = thisBtn.hasClass('active') && listHashContainer.length > 0; 54 console.log(isRemovingFromList); 55 if (isRemovingFromList) { 56 var list = listHashContainer.data('listhash'); 57 58 thisBtn.toggleClass('active', false); 59 60 syncFavorites('delete', productId, start, thisBtn, list); 61 } else { 62 63 loadFavoriteList(null, null, productId, start); 64 } 65 66 } else { 67 var isRemoving = thisBtn.hasClass('active'); 68 var performAction = isRemoving ? 'delete' : 'add'; 69 70 $(this).toggleClass('active', !isRemoving); 71 72 if (!isRemoving) { 73 loadFavoriteList(null, null, productId, start); 74 75 let modal = $("#favoriteListPopup"); 76 modal.data("product", productId); 77 modal.data("start", start); 78 modal.data("btn", thisBtn); 79 modal.modal("show"); 80 return; 81 } 82 83 syncFavorites(performAction, productId, start, thisBtn, null); 84 } 85 86 return false; 87 }); 88 89 $(document).on("hide.bs.modal", "#favoriteListPopup", function () { 90 let modal = $("#favoriteListPopup"); 91 console.log($(".favoriteSavedPopup").is(":visible")); 92 93 if (!added) { 94 $(".favorite[data-product='" + modal.data("product") + "'][data-date='" + modal.data("start") + "']").removeClass("active"); 95 let notSavedPopup = $(".favoriteNotSavedPopup") 96 notSavedPopup.slideToggle(); 97 if (timeout) { 98 clearTimeout(timeout); 99 notSavedPopup.slideToggle(); 100 } 101 timeout = setTimeout(function () { 102 if (notSavedPopup.is(":visible")) { 103 notSavedPopup.slideToggle() 104 } 105 timeout = false; 106 }, 5000) 107 } 108 }) 109 110 $(document).on("click", "#favoriteLoginButton", function () { 111 added=true; 112 let modal = $("#loginModal"); 113 modal.data('favorite_product', $(this).parents('.modal').data('product')); 114 modal.data('start', $(this).parents('.modal').data('start')); 115 modal.data('btn', $(this).parents('.modal').data('btn')); 116 modal.modal("show"); 117 $("#favoriteListPopup").modal("hide"); 118 }) 119 120 $(document).on("click", "#favoriteRegisterButton", function () { 121 added=true; 122 let modal = $("#registerModal"); 123 modal.data('favorite_product', $(this).parents('.modal').data('product')); 124 modal.data('start', $(this).parents('.modal').data('start')); 125 modal.data('btn', $(this).parents('.modal').data('btn')); 126 modal.modal("show"); 127 $("#favoriteListPopup").modal("hide"); 128 }) 129 130 $(document).on("click", ".favoriteAddList", function (e) { 131 added = true; 132 let modal = $("#favoriteListPopup"); 133 let productId = modal.data("product") 134 let start = modal.data("start"); 135 let btn = modal.data("btn") 136 let list = $(this).data("listid"); 137 modal.modal("hide") 138 139 syncFavorites("add", productId, start, btn, list); 140 }) 141 142 $(document).on("click", "#newListButton", function (e) { 143 $("#newListContent").show(); 144 $("#listContent").hide(); 145 }); 146 147 moveElements(); 148 $(window).resize(function () { 149 moveElements(); 150 }); 151 152 setFavoriteProducts(); 153 154 $('.swipe-front').each(function() { 155 var card = $(this); 156 var startX, currentX; 157 var threshold = 100; // Distance to swipe before it snaps open 158 var isDragging = false;
159 160 card.on('touchstart', function(e) { 161 // jQuery wrapper for touch event 162 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 163 startX = touch.clientX; 164 card.css('transition', 'none'); // Remove transition for instant follow 165 isDragging = true; 166 }); 167 168 card.on('touchmove', function(e) { 169 if (!isDragging) return; 170 171 var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; 172 currentX = touch.clientX; 173 var diff = currentX - startX; 174 175 // Only allow swiping left (negative values) 176 if (diff < 0) { 177 // If swiping horizontally, stop page scroll (optional but recommended) 178 if (Math.abs(diff) > 10 && e.cancelable) { 179 // e.preventDefault(); 180 } 181 card.css('transform', 'translateX(' + diff + 'px)'); 182 } 183 }); 184 185 card.on('touchend', function(e) { 186 if (!isDragging) return; 187 isDragging = false; 188 189 var diff = currentX - startX; 190 card.css('transition', 'transform 0.3s ease-out'); // Smooth snap 191 192 // If swiped left more than threshold, snap open to -120px 193 if (diff < -threshold) { 194 card.css('transform', 'translateX(-120px)'); 195 } else { 196 card.css('transform', 'translateX(0)'); 197 } 198 }); 199 }); 200 201 $('body').on('click', '.delete-action', function() { 202 var btn = $(this); 203 var container = btn.closest('.swipe-container'); 204 var listId = container.data('id'); 205 var listCount = parseInt(container.data('listcount')) || 0; 206 if(listCount > 0) { 207 Lobibox.notify('error', { 208 pauseDelayOnHover: true, 209 size: 'mini', 210 rounded: true, 211 delayIndicator: true, 212 delay: isMobile ? 4500 : 3500, 213 icon: 'material-icons favorite-icon', 214 continueDelayOnInactiveTab: false, 215 position: isMobile ? 'bottom center custom-lobi' : 'top right', 216 msg: trans("delete_favorites_first") 217 }); 218 container.find('.swipe-front').css('transform', 'translateX(0)'); 219 return; 220 } 221 222 deleteList(listId, container); 223 }); 224}); 225 226function deleteList(listId, container) { 227 if (!confirm(trans("sure_to_delete_list"))) { 228 container.find('.swipe-front').css('transform', 'translateX(0)'); 229 return; 230 } 231 232 $.ajax({ 233 url: endpoint, 234 method: 'POST', 235 dataType: 'json', 236 data: { 237 action: 'deleteList', 238 list_id: listId 239 }, 240 success: function (response) { 241 if (response.success) { 242 container.css('transition', 'all 0.5s ease').css('opacity', '0'); 243 244 setTimeout(function() { 245 container.slideUp(300, function() { 246 $(this).remove(); 247 }); 248 }, 500); 249 250 251 Lobibox.notify('success', { 252 size: 'mini', 253 rounded: true, 254 icon: 'material-icons check', 255 msg: trans("list_deleted_successfully") 256 }); 257 258 } else { 259 alert(trans("something_went_wrong_try_again")); 260 container.find('.swipe-front').css('transform', 'translateX(0)'); 261 } 262 }, 263 error: function (xhr, status, error) { 264 console.error("Delete failed:", error); 265 Lobibox.notify('error', { 266 size: 'mini', 267 rounded: true, 268 icon: 'material-icons check', 269 msg: trans("something_wrong") 270 }); 271 container.find('.swipe-front').css('transform', 'translateX(0)'); 272 } 273 }); 274} 275 276function saveFavoriteList() { 277 let name = $("#nameInput").val(); 278 let modal = $("#favoriteListPopup"); 279 let productId = modal.data("product") 280 let start = modal.data("start"); 281 let btn = modal.data("btn") 282 $.ajax({ 283 url: endpoint, 284 method: 'POST', 285 dataType: 'json', 286 data: { 287 action: 'createList', 288 name: name, 289 }, 290 success: function (response) { 291 if (response.success) { 292 added = true; 293 syncFavorites("add", productId, start, btn, response.listId); 294 modal.modal("hide"); 295 $("#newListContent").hide(); 296 $("#listContent").show(); 297 } else { 298 Lobibox.notify('error', { 299 size: 'mini', 300 rounded: true, 301 icon: 'material-icons check', 302 msg: response.message 303 }); 304 } 305 let saveButton = $("#saveFavoriteListButton") 306 saveButton.html(saveButton.data("text")) 307 }, 308 error: function (xhr, status, error) { 309 Lobibox.notify('error', { 310 size: 'mini', 311 rounded: true, 312 icon: 'material-icons check', 313 msg: trans("something_wrong") 314 }); 315 } 316 }); 317} 318 319function updateHeaderCount(count) { 320 var favoriteBlock = $('#favoriteProductContent'); 321 var countSpan = favoriteBlock.find('a span:not(.material-icons):not(.logout)'); 322 if (countSpan.length === 0) { 323 countSpan = favoriteBlock.find('a div.li span'); 324 } 325 var linkContainer = favoriteBlock.find('a'); 326 var breakBar = $('.break-bar'); 327 var toggler = $('.navbar-toggler'); 328 329 countSpan.html(count); 330 331 if (count > 0) { 332 linkContainer.show(); 333 favoriteBlock.find('a .li').show(); 334 breakBar.css('display', 'inline-block'); 335 toggler.addClass('favoriteEnabled'); 336 } else { 337 linkContainer.hide(); 338 breakBar.hide(); 339 toggler.removeClass('favoriteEnabled'); 340 } 341} 342 343 344function addFavoriteAfterLogin() { 345 let modal = $("#loginmodal:visible, #registerModal:visible"); 346 if (modal.data("favorite_product")) { 347 syncFavorites("add", modal.data("favorite_product"), modal.data("start"), modal.data("btn")) 348 } 349} 350 351function syncFavorites(performAction, productId = 0, start = false, btn = false, list = null) { 352 $.ajax({ 353 url: endpoint, 354 method: 'POST', 355 dataType: 'json', 356 data: { 357 action: 'syncFavorite', 358 perform: performAction, 359 product_id: productId, 360 start: start, 361 list: list 362 }, 363 success: function (response) { 364 var newCount = response.favoriteCount || 0; 365 366 updateHeaderCount(newCount); 367 368 var date = new Date(); 369 date.setTime(date.getTime()+(1500*60*1000)); 370 var expires = "; expires="+date.toGMTString(); 371 372 document.cookie = "favoriteProduct=" + newCount + expires +"; path=/"; 373 if (window.flutter_inappwebview !== undefined) { 374 console.log('favoritesUpdated'); 375 window.flutter_inappwebview.callHandler('favoritesUpdated'); 376 } 377 378 379 if (performAction == "delete" && $("[data-listhash]").length > 0) { 380 window.location.reload(); 381 } 382 383 if (performAction === 'add' && btn) { 384 added = false;
385 var message = trans("save_in_link", { 386 "LINK": '<a href="' + response.list_url + '"><u>' + response.list + '</u></a>' 387 }) 388 let savedPopup = $(".favoriteSavedPopup") 389 let savedTextElement = savedPopup.find(".savedText"); 390 let img = $(".favoriteSavedPopup").find("img"); 391 savedTextElement.html(message); 392 savedPopup.slideToggle() 393 img.attr("src", response.image); 394 395 if (timeout2) { 396 clearTimeout(timeout2); 397 savedPopup.slideToggle() 398 } 399 timeout2 = setTimeout(function () { 400 if (savedPopup.is(":visible")) { 401 savedPopup.slideToggle() 402 } 403 timeout2 = false; 404 }, 10000) 405 406 } else if(performAction === 'sync') { 407 408 favoriteProducts = response.favoriteProducts; 409 410 $('body .favorite').each(function() { 411 var productId = $(this).data('product') + '-' + $(this).data('date'); 412 if(favoriteProducts.includes(productId)) { 413 $(this).addClass('active'); 414 } else { 415 $(this).removeClass('active'); 416 } 417 }) 418 } 419 }, 420 error: function (xhr, status, error) { 421 console.log("Favorite sync failed:", error); 422 console.log("Status Code:", xhr.status); 423 console.log("Server Response:", xhr.responseText); 424 if(productId > 0) { 425 $(".favorite[data-product='" + productId + "']").toggleClass('active', performAction === 'delete'); 426 } 427 } 428 }); 429} 430 431function moveElements() { 432 var domWidth = $(window).width(); 433 var favoriteBlock = $('#favoriteProductContent'); 434 if (domWidth > 1199) { 435 $('.favoriteProductContentDesktop').html(favoriteBlock.outerHTML); 436 $('.favoriteProductContentMobile').html(''); 437 } else { 438 $('.favoriteProductContentDesktop').html(''); 439 $('.favoriteProductContentMobile').html(favoriteBlock.outerHTML); 440 } 441} 442 443function setFavoriteProducts() { 444 syncFavorites('sync'); 445} 446 447function loadFavoriteList(favorite_id, list_id, product_id = null, start = null) { 448 449 $('#favoritePopup .modal-body').html('<div class="w-100 text-center py-5"><span style="font-size:36px;" class="mi-spin material-icons">progress_activity</span></div>') 450 if (isBusiness) { 451 $('#favoritePopup').modal("show"); 452 } 453 454 455 $.ajax({ 456 url: endpoint, 457 method: 'POST', 458 dataType: 'json', 459 data: { 460 action: 'loadFavoriteList', 461 favorite_id: favorite_id, 462 list_id: list_id, 463 product_id: product_id, 464 start: start 465 }, 466 success: function (response) { 467 if(response.success) { 468 if (isBusiness) { 469 $('#favoritePopup .modal-body').html(response.html) 470 } else { 471 $('#favoriteListPopup #favoriteLists').html(response.html) 472 } 473 474 } else { 475 $('#favoritePopup .modal-body').html(`<p class="p-3 text-center">${trans("something_wrong_not_logged_in")}</p>`); 476 } 477 } 478 }) 479} 480 481function submitFavoriteForm(e, form) { 482 e.preventDefault(); 483 484 var selectedLists = []; 485 486 $(form).find('input[name="favorite_list[]"]:checked').each(function() { 487 selectedLists.push($(this).val()); 488 }); 489 490 if(selectedLists.length === 0) { 491 var radioVal = $(form).find('input[name="favorite_list"]:checked').val(); 492 if(radioVal) { 493 selectedLists.push(radioVal); 494 } 495 } 496 497 $.ajax({ 498 url: endpoint, 499 method: 'POST', 500 dataType: 'json', 501 data: { 502 action: 'saveFavorite', 503 favorite_id: $(form).find('input[name=favorite_id]').val(), 504 product_id: $(form).find('input[name=product_id]').val(), 505 start: $(form).find('input[name=start]').val(), 506 favorite_list: selectedLists, 507 new_name: $(form).find('input[name=new_name]').val(), 508 }, 509 success: function (response) { 510 511 Lobibox.notify(response.success ? 'success' : 'error', { 512 pauseDelayOnHover: true, 513 size: 'mini', 514 rounded: true, 515 delayIndicator: true, 516 delay: 3500, 517 icon: 'material-icons favorite-icon', 518 continueDelayOnInactiveTab: false, 519 position: 'top right', 520 msg: response.message 521 }); 522 523 $('#favoritePopup').modal("hide"); 524 525 if(response.success) { 526 syncFavorites('sync'); 527 } 528 } 529 }) 530} 531 532function saveFavoriteNamePopup() { 533 const modal = $("#favoriteNamePopup"); 534 const name = modal.find("input[type='text']").val(); 535 const listId = modal.find("[name=list_id]").val(); 536 modal.modal("hide"); 537 538 $.ajax({ 539 url: endpoint, 540 method: 'POST', 541 dataType: 'json', 542 data: { 543 action: 'changeName', 544 list_id: listId, 545 name, 546 }, 547 success: function (response) { 548 if (response.success) { 549 window.location.reload(); 550 } 551 }, 552 error: function (xhr, status, error) { 553 console.error("Delete failed:", error); 554 } 555 }); 556} 557 558function deleteFavoriteList() { 559 const modal = $("#favoriteDeletePopup"); 560 const listId = modal.find("[name=list_id]").val(); 561 modal.modal("hide"); 562 $.ajax({ 563 url: endpoint, 564 method: 'POST', 565 dataType: 'json', 566 data: { 567 action: 'deleteList', 568 list_id: listId 569 }, 570 success: function (response) { 571 if (response.success) { 572 window.location.reload(); 573 } else { 574 console.log("DELETE DID NOT WORKKK") 575 } 576 577 }, 578 error: function (xhr, status, error) { 579 console.error("Delete failed:", error); 580 } 581 }); 582}
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.