1/* 2 3Project: www.staycobblestone.com 4Conrtibutors: Jeramiah Harland 5tue jul 16 6.19 pm 6---------------------- 7Contents 8---------------------- 9 101. General 112. Mobile 123. Scroll Horizontal Navigation 134. Calendar/Booking 145. Read More/Read Less 156. Lightbox & Scrolling 167. Group Booking 178. Gallery Script 189. States/Cities Dropdown 1910. TripAdvisor 2011. Google Places API - Explore and Location Maps 2112. 404 Page 2213. Helper Functions 2314. Guest Experience Survey Functions 2415. Anaytics Tracking 2516. Sliders 26 27*/ 28 29/*jslint bitwise: true */ 30 31/*global ga */ 32 33/*global google */ 34 35/*global cfsData */ 36 37/*global jsData */ 38 39/*jshint unused:false*/ 40 41jQuery(document).ready(function ($) { 42 43 "use strict"; 44 45// -------------------------------------------------------------------------- 46// --------------------------------------------------------- General 47// -------------------------------------------------------------------------- 48 49 //Adds placeholders for browsers that don't support them 50 try { 51 52 $('input, textarea').placeholder(); 53 54 } catch (err) { 55 } 56 57 //Give tel-links 'phone_number' class 58 $('a[href^=tel]').each(function () { 59 $(this).addClass('phone_number'); 60 }); 61 $(window).on('load', function () { 62 $('a[href^=tel]').each(function () { 63 $(this).addClass('phone_number'); 64 }); 65 }); 66 67 // Prevent all form submit events 68 // We will handle these on our own 69 $('form').submit(function (e) { 70 e.peventDefault(); 71 }); 72 73 // Booking link click from anywhere but the form 74 $('body').on('click', '.book-now', function (e) { 75 76 e.preventDefault(); 77 78 // Track Event 79 trackEvent('Reservations', 'book-link', 'Booking Link', 5, true); 80 81 var bookingIDPattern = /\b\d{5}\b/g; 82 83 if ($(this).attr('href').indexOf('#') > -1 && bookingIDPattern.test($(this).attr('href'))) { 84 85 var bookingID = $(this).attr('href').match(bookingIDPattern)[0]; 86 openBooking('', bookingID); 87 88 } else { 89 90 openBooking(); 91 92 } 93 }); 94 95 // centerSlideshows 96 var centerSlideshows = (function () { 97 98 var resizeTimer; 99 100 //Center Slideshows 101 function centerSlideshow() { 102 var windowWidth = viewport().width; 103 var marginDifference = 0; 104 var $targetImage = $('#property-slider .slide-image, #page-404-header .slide-image'); 105 if (windowWidth <= 2000) { // width of image 106 107 if (windowWidth <= 1000) { 108 $targetImage = $('#mobile-slider .image'); 109 } 110 111 marginDifference = -1 * ($targetImage.width() - windowWidth) / 2; 112 $targetImage.css('margin-left', marginDifference + 'px'); 113 114 } else { 115 $targetImage.css('margin-left', '0px'); 116 117 } 118 } 119 120 $(window).on('resize', function (e) { 121 122 clearTimeout(resizeTimer); 123 resizeTimer = setTimeout(function () { 124 125 centerSlideshow(); 126 127 }, 100); 128 129 }); 130 131 $(window).load(function () { 132 centerSlideshow(); 133 }); 134 135 function viewport() { 136 var e = window, a = 'inner'; 137 if (!('innerWidth' in window)) { 138 a = 'client'; 139 e = document.documentElement || document.body; 140 } 141 return {width: e[a + 'Width'], height: e[a + 'Height']}; 142 } 143 144 // return 145 return { 146 center: centerSlideshow 147 }; 148 149 })(); // End Center Slideshow 150 151 // Center Slideshow as soon as document is ready 152 centerSlideshows.center(); 153 154// -------------------------------------------------------------------------- 155// -------------------------------------------------------------- Mobile 156// -------------------------------------------------------------------------- 157 158 // Menu 159 var mobileMenu = (function () { 160 161 var mobileVisible = false; 162 163 $('#mobile-menu-button').click(function () { 164 $('#mobile-menu, html').addClass('menu-open'); 165 mobileVisible = true; 166 }); 167 168 $('#mobile-menu-close').click(function () { 169 if ($('#mobile-menu, html').hasClass('menu-open')) { 170 $('#mobile-menu, html').removeClass('menu-open'); 171 mobileVisible = false;
172 } 173 }); 174 175 $(document).mouseup(function (e) { 176 if (mobileVisible && !$(e.target).is('#mobile-menu *, #mobile-menu') || hasCustomClass(e.target)) { 177 if ($('#mobile-menu, html').hasClass('menu-open')) { 178 $('#mobile-menu, html').removeClass('menu-open'); 179 mobileVisible = false; 180 } 181 } 182 }); 183 184 $('#mobile-menu').on('click', '.mobile-icon', function (e) { 185 e.preventDefault(); 186 mobileIconClick(this); 187 }); 188 189 function mobileIconClick(icon) { 190 191 var a = $(icon).find('a'); 192 var span = $(icon).find('span'); 193 194 if (a.length > 0) { 195 var href = a.attr('href'); 196 window.open(href, '_blank'); 197 } else if (span.length > 0) { 198 toggleMobileIconContent(span.text()); 199 setTimeout(function () { 200 toggleMobileIconContent(); 201 }, 2000); 202 } 203 204 } 205 206 function toggleMobileIconContent(content) { 207 208 var contentContainer = $('#mobile-icon-overlay'); 209 210 if (contentContainer.hasClass('open')) { 211 contentContainer.removeClass('open'); 212 contentContainer.empty(); 213 } else { 214 var c = $('<span>').text(content); 215 contentContainer.append(c); 216 contentContainer.addClass('open'); 217 } 218 219 } 220 221 })(); // End mobileMenu 222 223// -------------------------------------------------------------------------- 224// ---------------------------------------------------------------- Captions 225// -------------------------------------------------------------------------- 226 227 $('.caption .icon-close').on('click', function () { 228 $(this).closest('.caption').stop().fadeOut(); 229 }); 230 231 232// -------------------------------------------------------------------------- 233// --------------------------------------------- Scroll Horizontal Navigation 234// -------------------------------------------------------------------------- 235 if ($('.scroll-this-main').length && $('.scroll-this-top').length || $('.scroll-this-bottom').length) { 236 237 // Protect scope 238 var scrollingFeatures = (function () { 239 240 var $main = $('.scroll-this-main'); 241 var $topNav = ($('.scroll-this-top').length !== -1) ? $('.scroll-this-top') : ''; 242 var $outerWrapper = $('#outer-wrapper'); 243 var $bottomNav = ($('.scroll-this-bottom').length !== -1) ? $('.scroll-this-bottom') : ''; 244 var $mainContent = ($main.find('.row, .slick-dots').length !== -1) ? $main.find('.row, .slick-dots') : ''; 245 var $images = ($main.find('.slide').children('img').length !== -1) ? $main.find('.slide').children('img') : ''; 246 var $phantom = ($('#blue-phantom').length !== -1) ? $('#blue-phantom') : ''; 247 var $adminBar = $('#wpadminbar'); 248 var loggedIn = $('body').hasClass('logged-in'); 249 250 // resize time 251 var rTime; 252 253 // Timeout 254 var timeout = false; 255 256 // milliseconds 257 var ms = 100; 258 259 function init() { 260 updateHeight(); 261 updateScrolling(); 262 } 263 264 function updateScrolling() { 265
266 // Window (viewport) 267 var $window = $(window); 268 269 // Sets the position where, once reached, the fixed class will be applied (stick to bottom) 270 var fixToBottomAt = $main.offset().top + $main.outerHeight(); 271 if ($bottomNav !== '') { 272 fixToBottomAt += $bottomNav.outerHeight(); 273 } 274 275 // Sets the position where, once reached, the fixed class will be applied (stick to top) 276 var fixToTopAt = $main.offset().top + $main.outerHeight(); 277 278 // Current scroll position plus window height 279 var windowBottomPos = $window.scrollTop() + $window.height(); 280 281 if (fixToBottomAt > windowBottomPos) { // When slider is bigger than the window 282 283 // fix to bottom 284 285 // Remove 'top' add 'fixed' 286 //$booking.removeClass('top').addClass('fixed'); 287 288 // For slide content 289 //var newBottom = Math.round(fixToBottomAt - windowBottomPos); 290 //$slideContent.css('bottom', newBottom); 291 292 // Remove top navigation to fixed 293 //$outerWrapper.css('margin-top', 0); 294 //$booking.css('top', 'auto'); 295 //$topNav.removeClass('fixed open'); 296 297 // Show phantom booking 298 //showPhantom(); 299 300 } else if ($window.scrollTop() > fixToTopAt) { // When scrollbar is passed the slider/booking 301 302 // Admin Bar 303 var adminBar = loggedIn && $adminBar.css('position') === 'fixed' ? Math.round($adminBar.outerHeight()) : 0; 304 305 // Add 'fixed' and 'top' 306 if ($bottomNav !== '') { 307 $bottomNav.addClass('fixed top'); 308 //$bottomNav.css('top', '0px'); 309 $bottomNav.css('top', (adminBar + 'px')); 310 if ($phantom !== '') { 311 showPhantom(); 312 } 313 } 314 315 if ($topNav !== '' && $topNav.is(':visible')) { 316 $bottomNav.css('top', (adminBar + Math.round($topNav.outerHeight())) + 'px'); 317 $topNav.addClass('fixed').css('top', adminBar); 318 $topNav.addClass('open'); 319 320 // Add top navigation to fixed 321 $outerWrapper.css('margin-top', Math.round($topNav.outerHeight()) + 'px'); 322 } 323 324 } else { // Booking is visible and so is the slider 325 326 if ($bottomNav !== '') { 327 // Remove 'fixed' 328 $bottomNav.removeClass('fixed'); 329 $bottomNav.css('top', 'auto'); 330 if ($phantom !== '') { 331 hidePhantom(); 332 } 333 } 334 335 if ($topNav !== '') { 336 // Remove top navigation to fixed 337 $topNav.removeClass('fixed open').css('top', 0); 338 } 339 340 if ($mainContent !== '') { 341 // Set bottom of content back to 0 342 $mainContent.css('bottom', 0); 343 } 344 345 $outerWrapper.css('margin-top', 0); 346 347 348 } 349 350 // Parallax 351 // if($images.height() > $slider.outerHeight()){ // When image is bigger than the slider 352 // 353 // var overflow = $images.height() - $slider.outerHeight(); 354 // 355 // var newTop = Math.round((overflow - $(window).scrollTop())*-1); 356 // $images.css('transform', 'translateY( ' + newTop/2 + 'px)'); 357 // 358 // }else{ 359 // $images.css('transform', 'none'); 360 // } 361 362 } 363 364 365 function showPhantom() { 366 $phantom.height($bottomNav.outerHeight()).show(); 367 } 368 369 function hidePhantom() { 370 $phantom.hide(); 371 } 372 373 function updateHeight() { 374 // Remove previous height attribute 375 $main.css('height', ''); 376 // Set the slide height as the active image height 377 378 var $slide = $main.find('.slide'); 379 380 if ($slide.length !== -1 && $slide.is(':visible')) { 381 382 var $img = $main.find('.slide-image'); 383 384 var newHeight = $img.height(); 385 386 $main.find('.slide').height(newHeight); 387 } 388 389 } 390 391 $(window).scroll(throttle(25, function () { 392 updateScrolling(); 393 })); 394 395 $(window).on('resize', function () { 396 updateContent(); 397 }); 398 399 $(window).on('load', function () { 400 updateHeight(); 401 }); 402 403 function updateContent() { 404 rTime = new Date(); 405 if (timeout === false) { 406 timeout = true; 407 setTimeout(resizeEnd, ms); 408 } 409 } 410 411 function resizeEnd() { 412 if (new Date() - rTime < ms) { 413 setTimeout(resizeEnd, ms); 414 } else { 415 timeout = false;
416 init(); 417 } 418 } 419 420 // return 421 return { 422 init: init, 423 updateHeight: updateHeight, 424 updateScroll: updateScrolling 425 }; 426 427 })(); // End scrollingFeatures 428 429 } 430 431// -------------------------------------------------------------------------- 432// -------------------------------------------------------- Calendar/Booking 433// -------------------------------------------------------------------------- 434 435 if ($('#interior-booking').length) { 436 437 var bookingCalendars = (function () { 438 439 var inputWidth = ($('#interior-booking').find('input.datepicker-checkin').outerWidth()) * -1; 440 441 function init() { 442 $('input.datepicker-checkin').Zebra_DatePicker({ 443 direction: true, 444 format: 'm/d/Y', 445 show_icon: false, 446 offset: [inputWidth, 0], 447 first_day_of_week: 0, 448 header_navigation: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], 449 pair: $('input.datepicker-checkout') 450 }); 451 452 $('input.datepicker-checkout').Zebra_DatePicker({ 453 direction: 1, 454 show_icon: false, 455 offset: [inputWidth, 0], 456 first_day_of_week: 0, 457 header_navigation: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], 458 format: 'm/d/Y' 459 }); 460 } 461 462 // return 463 return { 464 init: init, 465 hide: hideCalendars, 466 467 }; 468 })(); // End bookingCalendar 469 470 // Set Calendars 471 $(window).on('load', function () { 472 bookingCalendars.init(); 473 }); 474 475 // Check availablity form submit 476 $('#check-availability .book-now-form').on('click', function (e) { 477 478 // Stop page from jumping to link 479 e.preventDefault(); 480 481 // Track Event 482 trackEvent('Reservations', 'book-form', 'Booking Form', 5, true); 483 484 // Get form elements as array 485 var formData = {}; 486 $.each($('#check-availability form').serializeArray(), function (i, field) { 487 formData[field.name] = field.value; 488 }); 489 490 if (($(this).attr('data-ivhtravel') == 'IVHTravel') || ($(this).attr('data-ivhtravel') == 'WebRezPro')) { 491 openNewBooking(formData, 0, $(this).attr('data-ivhtravel')); 492 return false; 493 } 494 495 openBooking(formData); 496 497 }); 498 } 499 500 function openNewBooking(data, bookingID, method) { 501 502 var bookingData = data; 503 if (typeof bookingData === 'undefined') { 504 bookingData = ''; 505 } 506 507 // Today 508 var date = new Date(); 509 var dd = date.getDate(); 510 var mm = date.getMonth() + 1; 511 var yyyy = date.getFullYear(); 512 513 var today = mm + '/' + dd + '/' + yyyy; 514 515 // Tomrrow 516 var tomorrow = mm + '/' + (dd + 1) + '/' + yyyy; 517 518 // Defaults 519 var chain = '7721'; 520 var step = '2'; 521 var shell = 'GDC'; 522 var hotel = '00000'; 523 var arrive = today; 524 var depart = tomorrow; 525 var rooms = '1'; 526 var no_adults = '2'; 527 var no_children = '0'; 528 var utm_source = 'IBC'; 529 var device = 'desktop'; 530 531 if ((typeof bookingID !== 'undefined') && (bookingID != 0)) { 532 hotel = bookingID; 533 } 534 535 if (bookingData !== '') { 536 // Get date and replace slashes 537 arrive = bookingData['check-in']; 538 depart = bookingData['check-out']; 539 } 540 541 // Fix slashes for URL 542 //arrive = arrive.replace(/\//g,'%2F'); 543 //depart = depart.replace(/\//g,'%2F'); 544 545 arrive = arrive.replace(/(\d\d)\/(\d\d)\/(\d{4})/, "$3-$1-$2"); 546 depart = depart.replace(/(\d\d)\/(\d\d)\/(\d{4})/, "$3-$1-$2"); 547 548 // Use cfsData if we have it 549 if (typeof cfsData !== "undefined" && cfsData !== null) { 550 551 chain = cfsData.property.chain_id; 552 hotel = cfsData.property.booking_id_ivhtravel; 553 554 } 555 556 if (hotel === '00000' || hotel === '') { 557 hotel = ''; 558 } else { 559 hotel = '?hotel=' + hotel; 560 } 561 var link = jQuery('span.book-now-form').attr('data-cstmurl'); 562 if (method == 'IVHTravel') { 563 arrive = '&checkin=' + arrive; 564 depart = '&checkout=' + depart; 565 566 rooms = '&rooms=' + rooms; 567 no_adults = '&no_adults=' + no_adults; 568 no_children = '&no_children=' + no_children; 569 utm_source = '&utm_source=' + utm_source; 570 device = '&device=' + device; 571 572 // Build URL 573 574 var url = link + hotel + arrive + depart + rooms + no_adults + n
574o_children + utm_source + device; 575 } 576 // var url = 'https://www.ivhtravel.com/hotel-centerstone-inn-doswell-at-kings-dominion-doswell-h87913.html'+hotel+arrive+depart+rooms+no_adults+no_children+utm_source+device; 577 if (method == 'WebRezPro') { 578 if (link.indexOf('?') < 1) link = link + '?'; 579 var url = link + '&date_from=' + arrive.replace(/-/g, '') + '&date_to=' + depart.replace(/-/g, '') + '&num_adults=' + no_adults + '&num_children=' + no_children; 580 } 581 // Open new window 582 // console.log(url); 583 window.open(url, "_self"); 584 } 585 586 function openBooking(data, bookingID) { 587 588 var bookingData = data; 589 if (typeof bookingData === 'undefined') { 590 bookingData = ''; 591 } 592 593 // Today 594 var date = new Date(); 595 var dd = date.getDate(); 596 var mm = date.getMonth() + 1; 597 var yyyy = date.getFullYear(); 598 599 var today = mm + '/' + dd + '/' + yyyy; 600 601 // Tomrrow 602 var tomorrow = mm + '/' + (dd + 1) + '/' + yyyy; 603 604 // Defaults 605 var chain = '7721'; 606 var step = '2'; 607 var shell = 'GDC'; 608 var hotel = '00000'; 609 var arrive = today; 610 var depart = tomorrow; 611 612 if (typeof bookingID !== 'undefined') { 613 hotel = bookingID; 614 } 615 616 if (bookingData !== '') { 617 // Get date and replace slashes 618 arrive = bookingData['check-in']; 619 depart = bookingData['check-out']; 620 } 621 622 // Fix slashes for URL 623 arrive = arrive.replace(/\//g, '%2F'); 624 depart = depart.replace(/\//g, '%2F'); 625 626 627 // Use cfsData if we have it 628 if (typeof cfsData !== "undefined" && cfsData !== null) { 629 630 chain = cfsData.property.chain_id; 631 hotel = cfsData.property.booking_id; 632 633 } 634 635 // Build strings for URL 636 chain = '?chain=' + chain; 637 step = '&step=' + step; 638 shell = '&shell=' + shell; 639 640 if (hotel === '00000' || hotel === '') { 641 hotel = ''; 642 } else { 643 hotel = '&hotel=' + hotel; 644 } 645 646 arrive = '&arrive=' + arrive; 647 depart = '&depart=' + depart; 648 649 var linkParam = ''; 650 if (typeof ga !== 'undefined') { 651 652 ga('require', 'linker'); 653 654 ga(function (tracker) { 655 linkParam = tracker.get('linkerParam'); 656 linkParam = '&' + linkParam; 657 }); 658 659 } 660 661 // Build URL 662 var url = 'https://be.synxis.com/' + chain + step + shell + hotel + arrive + depart + linkParam; 663 664 // Open new windows 665 window.open(url, "_self"); 666 667 // Open in same window 668 window.location(url); 669 670 } 671 672// -------------------------------------------------------------------------- 673// ----------------------------------------------------- Read More/Read Less 674// -------------------------------------------------------------------------- 675 676 if ($('#read-container').length) { 677 // Hide content area 678 if ($('#interior-main .content').children().length > 2) { 679 toggleViewInit(); 680 } else { 681 $('#read-container').hide(); 682 } 683 } 684 685 function toggleView() { 686 687 // Track Event 688 trackEvent('Interaction', 'read-more-less', 'Read Property Description'); 689 690 // Get number of visible children 691 var numVisible = $('#interior-main .content').children(':visible').length; 692 // Hide read more/read less buttons 693 $('#read-container').children().hide(); 694 // if there is only one visible child 695 // fade all children in otherwise only show first p 696 if (numVisible === 2) { 697 $('#interior-main .content').find('p:not(:nth-child(-n+2))').stop().slideDown(); 698 $('#read-container .less').show(); 699 } else { 700 $('#interior-main .content').find('p:not(:nth-child(-n+2))').stop().slideUp(); 701 $('#read-container .more').show(); 702 } 703 } 704 705 function toggleViewInit() { 706 // Hide all children except first on initial load; 707 $('#interior-main .content').find('p:not(:nth-child(-n+2))').hide(); 708 $('#read-container .more').show(); 709 } 710 711 $('#read-container .less, #read-container .more').on('click', function () { 712 toggleView(); 713 }); 714 715// -------------------------------------------------------------------------- 716// --------------------------------------------------------- Weather 717// -------------------------------------------------------------------------- 718 719 // initWeather runs on document ready and sets the weather 720 $(window).on('load', function () { 721 if ($('#property-weather').length && typeof cfsData !== "undefined" && cfsData !== null) { 722 initWeather(); 723 } 724 }); 725 726 function initWeather() { 727 var html = ''; 728 $.ajax({ 729 url: jsData.ajaxURL, 730 type: 'POST', 731 data: { 732 action: 'get_weather', 733 latitude: cfsData.map.latitude, 734 longitude: cfsData.map.longitude 735 }, 736 dataType: 'json', 737 async: true 738 }) 739 .success(function (response) { 740 if (response.error) { 741 hideWeather(); 742 } else { 743 var weather = getCondDetails(response); 744 html = '<i class="' + weather.icon + '"></i>'; 745 html += '<span class="temp">' + weather.temp + '°</span>'; 746 html += windChill(weather.feelslike); 747 html += '<span class="status">
747' + weather.text + '</span>'; 748 $('#property-weather').html(html); 749 } 750 }) 751 .fail(function (error) { 752 hideWeather(); 753 }); 754 755 } 756 757 function hideWeather() { 758 $('#property-weather').hide(); 759 } 760 761 function windChill(temp) { 762 var display = ''; 763 if (temp < 0) { 764 display = '<span id="wind-chill">(Feels Like ' + temp + '°)</span>'; 765 } 766 return display; 767 } 768 769 function getCondDetails(current) { 770 771 // Create weather object to return 772 var weather = { 773 temp: current.temp, 774 icon: '', 775 text: current.summary, 776 feelslike: current.feelslike 777 }; 778 779 switch (current.icon) { 780 781 case 'clear-day': 782 weather.icon = 'wi wi-day-sunny'; 783 break; 784 785 case 'clear-night': 786 weather.icon = 'wi wi-night-clear'; 787 break; 788 789 case 'rain': 790 weather.icon = 'wi wi-rain'; 791 break; 792 793 case 'snow': 794 weather.icon = 'wi wi-snow'; 795 break; 796 797 case 'sleet': 798 weather.icon = 'wi wi-sleet'; 799 break; 800 801 case 'wind': 802 weather.icon = 'wi wi-windy'; 803 break; 804 805 case 'fog': 806 weather.icon = 'wi wi-fog'; 807 break; 808 809 case 'cloudy': 810 weather.icon = 'wi wi-cloudy'; 811 break; 812 813 case 'partly-cloudy-day': 814 weather.icon = 'wi wi-day-cloudy'; 815 break; 816 817 case 'partly-cloudy-night': 818 weather.icon = 'wi wi-night-cloudy'; 819 break; 820 821 case 'hail': 822 weather.icon = 'wi wi-hail'; 823 break; 824 825 case 'thunderstorm': 826 weather.icon = 'wi wi-thunderstorm'; 827 break; 828 829 case 'tornado': 830 weather.icon = 'wi wi-tornado'; 831 break; 832 833 default: 834 weather.icon = 'wi wi-cloudy'; 835 break; 836 837 } 838 839 return weather; 840 841 } 842 843// -------------------------------------------------------------------------- 844// ---------------------------------------------------- Lightbox & Scrolling 845// -------------------------------------------------------------------------- 846 847 // Lightbox overlay - Scroll 848 $(window).scroll(throttle(100, function () { 849 if ($('.current-open').length && $('.current-open').is(':visible')) { 850 scrollOverlay('slow'); 851 } 852 })); 853 854 // Lightbox overlay - Functions 855 function scrollOverlay(speed, callback) { 856 // Optional Speed 857 speed = speed; 858 if (typeof speed === 'undefined') { 859 speed = 250; 860 } 861 862 // Content container 863 var $scrollObject = $(".current-open"); 864 var topOffset = 25; 865 866 // Variables 867 var distanceToBottom = $(document).height() - $scrollObject.offset().top - $scrollObject.outerHeight(); 868 var distanceFromTop = $(window).scrollTop() + topOffset + $scrollObject.outerHeight(); 869 var overFlowHeight = distanceFromTop + topOffset - $(document).height(); 870 871 // Scroll if visible element can't be seen in viewport 872 // or if element is hidden, then always scroll it 873 // this makes sure it's always accurately positioned when it appears 874 if (!$($scrollObject.find('.scroll-marker')).isOnScreen(0.1, 0.1) || !$scrollObject.is(':visible')) { 875 if (overFlowHeight > 0) { 876 $scrollObject.stop().animate({ 877 top: ($(window).scrollTop() - overFlowHeight) + 'px' 878 }, speed); 879 } else { 880 $scrollObject.stop().animate({ 881 top: ($(window).scrollTop() + topOffset) + 'px' 882 }, speed); 883 } 884 } 885 886 // Optional Callback function 887 if (typeof callback === 'function' && callback()) { 888 callback(); 889 } 890 } 891 892 // Lightbox click 893 $(document).on('click', '.open-lightbox', function (e) { 894 e.preventDefault(); 895 openLightbox(this); 896 }); 897 898 // Lightbox open 899 function openLightbox(e) { 900 901 var getTarget = ''; 902 if (typeof e === 'string') { 903 if (e.indexOf('.') === -1 && e.indexOf('#') === -1) { 904 getTarget = '#' + e; 905 } else { 906 getTarget = e; 907 } 908 } else if (typeof $(e).attr('id') !== 'undefined' && $(e).attr('id') !== null) { 909 getTarget = '#' + $(e).attr('id'); 910 } else { 911 getTarget = $(e).attr('href'); 912 } 913 914 var targetObject = getTarget + '-lightbox'; 915 $(targetObject).addClass('current-open'); 916 $('#page-overlay').fadeIn(function () { 917 // Use the same function from the lightbox overlay 918 scrollOverlay(100); 919 $(targetObject).css('display', 'block').animate({opacity: 1}); 920 }); 921 } 922 923 // Lightbox close 924 function closeLightbox(e) { 925 if (!$(e.target).parents('.lightbox-content').length || $(e.target).hasClass('lightbox-close')) { 926 $('#page-overlay, .lightbox-content').stop().fadeOut(function () { 927 $('.lightbox-content').removeClass('current-open'); 928 }); 929 } 930 } 931 932 // Click page overlay or close button 933 $('#page-overlay, .lightbox-close').click(function () { 934 closeLightbox(this); 935 }); 936 937 // Escape button press 938 $(document).keyup(function (e) { 939 if (!$(document.activeElement).closest('.lightbox-content').length || e.which === 27 && $('#page-overlay').length) { 940 closeLightbox(this); 941 } 942 }); 943 944// -------------------------------------------------------------------------- 945// --------------------------------------------------------- Privacy Alert 946// -------------------------------------------------------------------------- 947 948 // Close Privacy Alert 949 $('#privacy-alert .close-privacy').on('click', function () { 950 $('#privacy-alert').hide(); 951 }); 952 953 $('#privacy-alert .privacy-consent-button').on('click', function (e) { 954 955 e.preventDefault(); 956 957 if (typeof jsData !== "undefined" && jsData !== null) { 958 959 // Use Ajax to submit to functions.php 960 $.ajax({ 961 url: jsData.ajaxURL, 962 type: 'POST', 963 data: { 964 action: 'add_privacy_consent' 965 }, 966 dataType: 'json', 967 async: true 968 }) 969 .success(function (response) { 970 971 if (!response.fail) { 972 973 // Set cookie 974 console.log('privacy cookie set'); 975 location.reload(); 976 977 } else { 978 console.log(response.fail); 979 } 980 981 982 }) 983 .fail(function (error) { 984 console.log(error); 985 }) 986 .always(function (response) { 987 988 console.log(response); 989 990 // Hide 991 $('#privacy-alert').hide(); 992 }); 993 994 } 995 996 }); 997 998 999// -------------------------------------------------------------------------- 1000// ---------------------------------------------------------- Group Booking 1001// -------------------------------------------------------------------------- 1002 1003 var groupBooking = (function () { 1004 1005 var working = false;
1006 1007 function validateFormData() { 1008 var data = $('#group-booking-form form'); 1009 if (validate.form(data)) { 1010 // Pass 1011 toggleLoader(); 1012 if (typeof jsData !== "undefined" && jsData !== null) { 1013 if (typeof cfsData !== "undefined" && cfsData !== null) { 1014 1015 // Fix obfuscated email 1016 cfsData.property.email = fixEmail(cfsData.property.email); 1017 1018 // Use Ajax to submit to functions.php 1019 $.ajax({ 1020 url: jsData.ajaxURL, 1021 type: 'POST', 1022 data: { 1023 action: 'send_mail_group_booking', 1024 form: data.serialize(), 1025 property: $.param(cfsData.property) 1026 }, 1027 dataType: 'json', 1028 async: true 1029 }) 1030 .success(function (response) { 1031 if (response.success) { 1032 success(); 1033 } else { 1034 fail(response.fail); 1035 } 1036 }) 1037 .fail(function (error) { 1038 fail(error); 1039 }) 1040 .always(function () { 1041 1042 // Virtual pageview 1043 // virtualPageView('group-booking-submit'); 1044 1045 toggleLoader(); 1046 working = false; 1047 }); 1048 } 1049 } // End If 1050 } else { 1051 // Fail 1052 working = false; 1053 console.log('validation failed'); 1054 } 1055 } 1056 1057 function createMessage(type, msg) { 1058 1059 var errorMessage = msg; 1060 if (typeof errorMessage === 'undefined') { 1061 errorMessage = ''; 1062 } 1063 1064 var $formContainer = $('#group-booking-form'); 1065
1066 var responseContainer = $('<div>').attr({ 1067 class: 'response-container ' + type.toLowerCase() 1068 }); 1069 1070 var messageIcon = $('<i>').attr({ 1071 class: 'fa' 1072 }).appendTo(responseContainer); 1073 1074 var message = $('<div>').attr({ 1075 class: 'cobble-font message' 1076 }).appendTo(responseContainer); 1077 1078 var messageDescription = $('<p>').attr({ 1079 class: 'message-description' 1080 }).appendTo(responseContainer); 1081 1082 var messageSubText = $('<span>').attr({ 1083 class: 'message-sub-text' 1084 }).text('').appendTo(responseContainer); 1085 1086 $formContainer.append(responseContainer); 1087 1088 1089 if (type === 'success') { 1090 1091 messageIcon.addClass('fa-check'); 1092 message.text('Success!'); 1093 messageDescription.text('Your message has been sent to ' + cfsData.property.city + '. Note: This does not book your stay, it only gets the information to ' + cfsData.property.city + '.'); 1094 messageSubText.text('Click here to send another'); 1095 1096 messageSubText.one('click', function () { 1097 toggleMessage(toggleForm()); 1098 }); 1099 1100 } else { 1101 1102 messageIcon.addClass('fa-exclamation'); 1103 message.text('There was an error!'); 1104 messageDescription.text('If you continue to experience an issue, please call or email ' + cfsData.property.city + ' directly.'); 1105 1106 if (errorMessage !== '') { 1107 console.log(errorMessage); 1108 messageSubText.text('Error Message: ' + errorMessage + ' - click here to try again'); 1109 } 1110 1111 messageSubText.one('click', function () { 1112 toggleMessage(toggleForm()); 1113 }); 1114 1115 } 1116 1117 } 1118 1119 function toggleMessage(callback) { 1120 1121 var $message = $('#group-booking-form .response-container'); 1122 1123 if ($message.is(':visible')) { 1124 $message.stop().fadeOut(function () { 1125 $message.remove(); 1126 // Optional Callback function 1127 if (typeof callback === 'function' && callback()) { 1128 callback(); 1129 } 1130 }); 1131 } else { 1132 $message.stop().fadeIn(function () { 1133 // Optional Callback function 1134 if (typeof callback === 'function' && callback()) { 1135 callback(); 1136 } 1137 }); 1138 } 1139 } 1140 1141 function toggleForm(callback) { 1142 1143 var $form = $('#group-booking-form .form'); 1144 1145 if ($form.is(':visible')) { 1146 $form.stop().fadeOut(function () { 1147 // Optional Callback function 1148 if (typeof callback === 'function' && callback()) { 1149 callback(); 1150 } 1151 }); 1152 } else { 1153 $form.stop().fadeIn(function () { 1154 // Optional Callback function 1155 if (typeof callback === 'function' && callback()) { 1156 callback(); 1157 } 1158 }); 1159 } 1160 } 1161 1162 function toggleLoader() { 1163 1164 var $loader = $('#group-booking-form .submit-button .loader'); 1165 1166 if ($loader.is(':visible')) { 1167 $loader.hide(); 1168 } else { 1169 $loader.css('display', 'inline-block'); 1170 } 1171 } 1172 1173 function success() { 1174 1175 var $formContainer = $('#group-booking-form'); 1176 1177 createMessage('success'); 1178 1179 toggleMessage(toggleForm()); 1180 } 1181 1182 function fail(msg) { 1183 1184 // For debugging 1185 msg = msg; 1186 if (typeof msg === 'undefined') { 1187 msg = 'fail'; 1188 } 1189 1190 createMessage('fail', msg); 1191 1192 toggleMessage(toggleForm()); 1193 } 1194 1195 // Group Booking Events 1196 $('.show-group-booking').click(function (e) { 1197 1198 e.preventDefault(); 1199 1200 validate.setRequired($('#group-booking-form form')); 1201 1202 openLightbox('group-booking'); 1203
1204 var inputWidth = ($('input.datepicker-group-arrive').outerWidth()) * -1; 1205 1206 $('input.datepicker-group-arrive').Zebra_DatePicker({ 1207 direction: true, 1208 format: 'm/d/Y', 1209 show_icon: false, 1210 offset: [inputWidth, 0], 1211 header_navigation: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], 1212 pair: $('input.datepicker-group-depart') 1213 }); 1214 1215 $('input.datepicker-group-depart').Zebra_DatePicker({ 1216 direction: 1, 1217 show_icon: false, 1218 offset: [inputWidth, 0], 1219 header_navigation: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], 1220 format: 'm/d/Y' 1221 }); 1222 }); 1223 1224 $('#group-booking-form .submit-button').on('click', function (e) { 1225 e.preventDefault(); 1226 if (!working) { 1227 1228 // Track Event 1229 trackEvent('Information Request', 'book-group', 'Group Booking Request', 3, true); 1230 1231 working = true; 1232 groupBooking.send(); 1233 } 1234 }); 1235 1236 $('#group-booking-form form :input').on('change', function (e) { 1237 validate.clearError(this); 1238 }); 1239 1240 // return 1241 return { 1242 send: validateFormData 1243 }; 1244 })(); // End groupBooking 1245 1246 1247// -------------------------------------------------------------------------- 1248// ---------------------------------------------------------- Gallery Script 1249// -------------------------------------------------------------------------- 1250 1251 if (typeof (cfsData) !== "undefined" && cfsData !== null) { 1252 1253 $('.show-gallery').click(function (e) { 1254 e.preventDefault(); 1255 1256 // Track Event 1257 trackEvent('Interaction', 'gallery-open', 'Viewed Photo Gallery'); 1258 1259 var scrollPos = 0; 1260 var galleryIndex = $(this).find('img').attr('class'); 1261 if (typeof galleryIndex !== "undefined" && galleryIndex !== null && galleryIndex.indexOf("index-") >= 0) { 1262 galleryIndex = galleryIndex.substring(galleryIndex.indexOf("index-")); 1263 galleryIndex = galleryIndex.replace(/\D/g, ''); 1264 galleryIndex = parseInt(galleryIndex); 1265 } else { 1266 galleryIndex = 0; 1267 } 1268 1269 $(this).lightGallery({ 1270 dynamic: true, 1271 mode: "lg-fade", 1272 download: false, 1273 enableTouch: true, 1274 dynamicEl: cfsData.gallery, 1275 index: galleryIndex 1276 }).on('onCloseAfter.lg', function (event) { 1277 scrollingFeatures.updateScroll(); 1278 }); 1279 }); 1280 1281 } 1282 1283// -------------------------------------------------------------------------- 1284// --------------------------------------------------- States/Cities Dropdown 1285// -------------------------------------------------------------------------- 1286 1287 var findACobblestone = (function () { 1288 1289 var statesCities; 1290 1291 function init() { 1292 if (typeof (jsData) !== "undefined" && jsData !== null) { 1293 $.ajax({ 1294 url: jsData.ajaxURL, 1295 type: 'GET', 1296 data: { 1297 action: 'get_states_cities' 1298 }, 1299 dataType: 'json', 1300 async: true 1301 }) 1302 .success(function (response) { 1303 statesCities = response; 1304 createDropdown(); 1305 }); 1306 } 1307 } 1308 1309 function createDropdown() { 1310 $.each(statesCities, function (key, value) { 1311 $.each(value.info, function (abbr, full) { 1312 $('<option>').attr({ 1313 value: abbr 1314 }).text(full).appendTo('#select-cobblestone-state'); 1315 }); 1316 }); 1317 } 1318 1319 function updateDropdown() { 1320 $('#select-cobblestone-city').find('option:not(.keep)').remove(); 1321 1322 var state = $('#select-cobblestone-state').val(); 1323 $.each(statesCities, function (key, value) { 1324 $.each(value.properties, function (city, info) { 1325 if (info.state === state) { 1326 $('<option>').attr({ 1327 value: info.link, 1328 id: cleanTitle(info.city) + '_' + cleanT
1328itle(info.state_abbr) + '_listing' 1329 }).text(info.city).appendTo('#select-cobblestone-city'); 1330 } 1331 }); 1332 }); 1333 1334 // todo try here 1335 // Event listener - Add event handler for clicking on place in list 1336 // document.getElementById(cleanTitle(info.city) + '_' + cleanTitle(info.state_abbr) + '_listing').addEventListener('click', function(event) { 1337 // //removed li[0], a refernce to the object from the earlier iteration 1338 // if(!working && !$(event.target).is('a')){ 1339 // working = true; 1340 // clickPlaceList(this); 1341 // } 1342 // }); 1343 1344 } 1345 1346 function updateHref() { 1347 1348 var $submitButton = $('#select-cobblestone .find-form'); 1349 1350 var $city = $('#select-cobblestone-city'); 1351 1352 var newHref = '#select-cobblestone'; 1353 1354 if ($city.val() !== 'select-city') { 1355 newHref = $city.val(); 1356 } 1357 1358 $submitButton.attr('href', newHref); 1359 1360 } 1361 1362 $('#select-cobblestone-state').on('change', function () { 1363 updateDropdown(); 1364 }); 1365 1366 $('#select-cobblestone-city').on('change', function () { 1367 updateHref(); 1368 }); 1369 1370 $('#select-cobblestone .find-form').on('click', function (e) { 1371 1372 var $state = $('#select-cobblestone-state'); 1373 var $city = $('#select-cobblestone-city'); 1374 1375 if ($city.val() === 'select-city' || $state.val() === 'select-state') { 1376 e.preventDefault(); 1377 } else { 1378 // Do nothing, let event fire normally 1379 // Track Event 1380 trackEvent('Navigation', 'select-state-city', 'Navigated Using Dropdowns', 1); 1381 } 1382 }); 1383 1384 // return 1385 return { 1386 init: init, 1387 update: updateDropdown 1388 }; 1389 1390 })(); // End findACobblestone 1391 1392 if ($('#select-cobblestone').length) { 1393 findACobblestone.init(); 1394 } 1395 1396// -------------------------------------------------------------------------- 1397// ------------------------------------------------------------- TripAdvisor 1398// -------------------------------------------------------------------------- 1399 1400 if ($('#tripadvisor-info').length && typeof cfsData !== "undefined" && cfsData !== null) { 1401 1402 initTripAdvisor(); 1403 1404 // On tab click 1405 $('#tripadvisor-info .tab').on('click', function () { 1406 1407 toggleTripAdvisor(); 1408 1409 }); 1410 1411 // Close TripAdvisor if clicking on something else 1412 $(document).click(function (e) { 1413 if ($('#tripadvisor-info').hasClass('open') && !$(e.target).closest('#tripadvisor-info').length) { 1414 closeTripAdvisor(); 1415 } 1416 }); 1417 } 1418 1419 function toggleTripAdvisor() { 1420 if ($('#tripadvisor-info').hasClass('open')) { 1421 closeTripAdvisor(); 1422 } else { 1423 1424 // Track Event 1425 trackEvent('Interaction', 'tripadvisor-open', 'Opened TripAdvisor Tab'); 1426 1427 openTripAdvisor(); 1428 } 1429 } 1430 1431 function openTripAdvisor() { 1432 $('#tripadvisor-info').addClass('open'); 1433 } 1434 1435 function closeTripAdvisor() { 1436 $('#tripadvisor-info').removeClass('open'); 1437 } 1438 1439 function initTripAdvisor() { 1440 1441 if (cfsData.property.tripadvisor_id !== '' && $('#tripadvisor-info').length) { 1442 getTripAdvisorData(cfsData.property); 1443 } 1444 } 1445 1446 function getTripAdvisorData(data) { 1447 1448 var id = data.tripadvisor_id; 1449 var key = 'da12eab3768f454e8e3caacebf19b779'; 1450 $.ajax({ 1451 url: "https://api.tripadvisor.com/api/partner/2.0/location/" + id + "?key=" + key, 1452 dataType: 'json', 1453 success: function (response) { 1454 // Show TripAdvisor tab 1455 $('#tripadvisor-info').css({ 1456 'visibility': 'visible' 1457 }).addClass('ready'); 1458 1459 // Show TripAdvisor in footer 1460 showTripAdvisorFooter(); 1461 1462 // Set content 1463 setTripAdvisorContent(response, data); 1464 1465 // Load TripAdvisor Comments 1466 setGesArray(response); 1467 } 1468 }) 1469 .fail(function () { 1470 $('#interior-explore .tab').css('top', 165 + 'px'); 1471 $('#tripadvisor-info').hide(); 1472 }); 1473 1474 } 1475 1476 function showTripAdvisorFooter() { 1477 var ta = $('<div>').attr({ 1478 class: 'sprite tripadvisor' 1479 }).insertAfter('#footer .copyright'); 1480 } 1481 1482 function setTripAdvisorContent(ta, data) { 1483 1484 // Ranking data 1485 if (ta.ranking_data !== null) { 1486 1487 // Rank string 1488 $('<h4>').attr({ 1489 id: 'ta-ranking-string' 1490 }).html(ta.ranking_data.ranking_string.replace(/(\#\d{1,4}
1490?\s)/g, '<span>$1</span>')).insertBefore('#ta-title'); 1491 1492 // Based on # of reviews 1493 $('<a>').attr({ 1494 href: ta.web_url, 1495 target: '_blank', 1496 id: 'ta-num-reviews' 1497 }).html('based on ' + ta.num_reviews + ' reviews').insertAfter('#ta-title'); 1498 1499 // Image rating 1500 $('<img>').attr({ 1501 id: '#ta-rating-src', 1502 src: ta.rating_image_url, 1503 alt: 'TripAdvisor Rating' 1504 }).insertAfter('#ta-title'); 1505 1506 // Read a review link 1507 $('#tripadvisor-read-review').attr("href", ta.web_url); 1508 1509 } 1510 1511 // Link to page url and display lightbox write a review 1512 if (data.tripadvisor_has_review) { 1513 $('#tripadvisor-write-review').addClass('open-lightbox'); 1514 } else { 1515 $('#tripadvisor-write-review').attr('href', ta.write_review); 1516 } 1517 1518 // Awards 1519 if ($(ta.awards).length > 0) { 1520 var awards = '<ul>'; 1521 $.each(ta.awards, function (i, v) { 1522 awards += '<li><img src="' + v.images.large + '" alt="' + v.display_name + '" /><span>' + v.display_name + '</span></li>'; 1523 }); 1524 awards += '</ul>'; 1525 $('#ta-awards').append(awards).show(); 1526 } 1527 1528 } 1529 1530// -------------------------------------------------------------------------- 1531// --------------------------- Google Places API - Explore and Location Maps 1532// -------------------------------------------------------------------------- 1533 1534 var googleMap = (function () { 1535 1536 var geocoder; 1537 var map; 1538 var mapMarkers = []; 1539 var tinyIcon; 1540 var defaultIcon; 1541 var selectedIcon; 1542 var propertyIcon; 1543 var defaultPropertyIcon; 1544 var zoomLevelDefaultIcon; 1545 var selectedMarker; 1546 var propertyMarker; 1547 var currentService; 1548 var mapContainer; 1549 var mapPanel; 1550 var mapData; 1551 var propertyData; 1552 var propertiesList; 1553 var metaData; 1554 var templateDir; 1555 var mapOptions; 1556 var mapType; 1557 var latLng; 1558 var bounds; 1559 var currentCenter; 1560 var newOffsetCenter; 1561 var initialSearch = true; 1562 var working = false; 1563 var isLoaded = false; 1564 var resizeTimer; 1565 1566 // For Google Places API Bug 1567 var placesListCheck = []; 1568 1569 // Map Styles 1570 var mapStyles = [{ 1571 "featureType": "administrative", 1572 "elementType": "geometry.fill", 1573 "stylers": [{"color": "#727545"}] 1574 }, { 1575 "featureType": "administrative", 1576 "elementType": "geometry.stroke", 1577 "stylers": [{"color": "#454f4b"}] 1578 }, { 1579 "featureType": "administrative.country", 1580 "elementType": "labels.text.fill", 1581 "stylers": [{"color": "#e3e4d3"}] 1582 }, { 1583 "featureType": "administrative.country", 1584 "elementType": "labels.text.stroke", 1585 "stylers": [{"color": "#75684d"}] 1586 }, { 1587 "featureType": "administrative.province", 1588 "elementType": "labels.text.fill", 1589 "stylers": [{"color": "#dbdcc8"}] 1590 }, { 1591 "featureType": "administrative.province", 1592 "elementType": "labels.text.stroke", 1593 "stylers": [{"color": "#858b64"}, {"weight": "2.5"}] 1594 }, { 1595 "featureType": "administrative.locality", 1596 "elementType": "labels.text.fill", 1597 "stylers": [{"color": "#ffffff"}] 1598 }, { 1599 "featureType": "administrative.locality", 1600 "elementType": "labels.text.stroke", 1601 "stylers": [{"color": "#767762"}, {"weight": "1.5"}] 1602 }, { 1603 "featureType": "administrative.neighborhood", 1604 "elementType": "labels.text.fill", 1605 "stylers": [{"visibility": "off"}] 1606 }, { 1607 "featureType": "landscape", 1608 "elementType": "geometry.fill", 1609 "stylers": [{"color": "#9fa274"}] 1610 }, { 1611 "featureType": "poi", 1612 "elementType": "geometry.fill", 1613 "stylers": [{"color": "#8e9160"}] 1614 }, {"featureType": "poi", "elementType": "labels", "stylers": [{"visibility": "off"}]}, { 1615 "featureType": "road", 1616 "elementType": "geometry.fill", 1617 "stylers": [{"color": "#8d946d"}] 1618 }, { 1619 "featureType": "road", 1620 "elementType": "geometry.stroke", 1621 "stylers": [{"color": "#848a64"}] 1622 }, { 1623 "featureType": "road", 1624 "elementType": "labels.text.fill", 1625 "stylers": [{"color": "#dbdcc8"}] 1626 }, { 1627 "featureType": "road", 1628 "elementType": "labels.text.stroke", 1629 "stylers": [{"color": "#778164"}] 1630 }, { 1631 "featureType": "road", 1632 "elementType": "labels.icon", 1633 "stylers": [{"hue": "#0090ff"}, {"saturation": "100"}, {"gamma": "0.37"}] 1634 }, { 1635 "featureType": "road.highway", 1636 "elementType": "geometry.fill", 1637 "stylers": [{"color": "#83765b"}] 1638 }, { 1639 "featureType": "road.highway", 1640 "elementType": "geometry.stroke", 1641 "stylers": [{"color": "#75684d"}] 1642 }, { 1643 "featureType": "road.highway", 1644 "elementType": "labels.text.fill", 1645 "stylers": [{"color": "#ffffff"}] 1646 }, { 1647 "featureType": "road.highway", 1648 "elementType": "labels.text.stroke", 1649 "stylers": [{"color": "#64573c"}] 1650 }, { 1651 "featureType": "road.arterial", 1652 "elementType": "geometry.fill", 1653 "stylers": [{"color": "#93876e"}] 1654 }, { 1655 "featureType": "road.arterial", 1656 "elementType": "geometry.stroke", 1657 "stylers": [{"color": "#8c8066"}] 1658 }, { 1659 "featureType": "road.arterial", 1660 "elementType": "labels.text.fill", 1661 "stylers": [{"color": "#dbdcc8"}] 1662 }, { 1663 "featureType": "road.arterial", 1664 "elementType": "labels.text.stroke", 1665 "stylers": [{"color": "#6d7759"}, {"weight": "2.5"}] 1666 }, { 1667 "featureType": "road.local", 1668 "elementType": "geometry.fill", 1669 "stylers": [{"color": "#8c926c"}] 1670 }, { 1671 "featureType": "road.local", 1672 "elementType": "geometry.stroke", 1673 "stylers": [{"color": "#7a805b"}] 1674 }, { 1675 "featureType": "road.local", 1676 "elementType": "labels.icon", 1677 "stylers": [{"visibility": "off"}] 1678 }, { 1679 "featureType": "transit", 1680 "elementType": "geometry.fill", 1681 "stylers": [{"color": "#8b8d83"}] 1682 }, { 1683 "featureType": "transit", 1684 "elementType": "labels.icon", 1685 "stylers": [{"visibility": "off"}] 1686 }, { 1687 "featureType": "water", 1688 "elementType": "geometry.fill", 1689 "stylers": [{"color": "#011f36"}] 1690 }, {"featureType": "water", "elementType": "labels.text.stroke", "stylers": [{"visibility": "off"}]}]; 1691 1692 // Loading
1693 function loading(msg, show, hide, targetLoader) { 1694 1695 // Optional Params 1696 msg = msg; 1697 if (typeof msg === 'undefined') { 1698 msg = ''; 1699 } 1700 1701 show = show; 1702 if (typeof show === 'undefined') { 1703 show = false; 1704 } 1705 1706 hide = hide; 1707 if (typeof hide === 'undefined') { 1708 hide = false; 1709 } 1710 1711 var loader = targetLoader; 1712 if (typeof loader === 'undefined') { 1713 loader = $('#map-loader'); 1714 } 1715 1716 // Update loading message 1717 if (msg !== '') { 1718 // Update loading message 1719 loader.find('.text').text(msg); 1720 } 1721 1722 // Show Loader 1723 if (show === true) { 1724 loader.show(); 1725 } 1726 1727 if (hide === true) { 1728 loader.hide(); 1729 } 1730 1731 } 1732 1733 // Initialize Explore Map 1734 function initExplore(data) { 1735 1736 // Set type 1737 mapType = 'explore'; 1738 1739 // Set container 1740 mapContainer = $('#interior-explore'); 1741 1742 // Set Panel 1743 mapPanel = mapContainer.find('.places-container'); 1744 1745 // Set mapData 1746 mapData = data.map; 1747 if (typeof mapData === 'undefined') { 1748 mapData = ''; 1749 } 1750 1751 // Set propertyData 1752 propertyData = data.property; 1753 if (typeof propertyData === 'undefined') { 1754 propertyData = ''; 1755 } 1756 1757 // Set metaData 1758 metaData = data.meta; 1759 if (typeof metaData === 'undefined') { 1760 metaData = ''; 1761 } 1762 1763 // Set templateDir 1764 templateDir = data.templateDir; 1765 if (typeof templateDir === 'undefined') { 1766 templateDir = ''; 1767 } 1768 1769 // Get initial LatLng 1770 if (typeof mapData.latitude !== 'undefined' && typeof mapData.longitude !== 'undefined') { 1771 latLng = initLatLng(mapData.latitude, mapData.longitude); 1772 } else { 1773 latLng = initLatLng(); 1774 } 1775 1776 // Set Options 1777 mapOptions = { 1778 zoom: 15, 1779 maxZoom: 18, 1780 minZoom: 6, 1781 center: latLng, 1782 backgroundColor: '#011f36', 1783 mapTypeId: google.maps.MapTypeId.ROADMAP, 1784 disableDefaultUI: false, 1785 scrollwheel: true, 1786 mapTypeControl: false, 1787 panControl: false, 1788 zoomControl: true, 1789 zoomControlOptions: { 1790 style: google.maps.ZoomControlStyle.DEFAULT, 1791 position: google.maps.ControlPosition.RIGHT_BOTTOM 1792 }, 1793 scaleControl: true, // fixed to BOTTOM_RIGHT 1794 streetViewControl: false, 1795 streetViewControlOptions: { 1796 position: google.maps.ControlPosition.RIGHT_TOP 1797 }, 1798 styles: mapStyles 1799 }; 1800 1801 // Open/Initialize Map 1802 openMap(init(mapOptions)); 1803 1804 } 1805 1806 // Initialize Find a Cobblestone Map 1807 function initFind() { 1808 1809 // Set type 1810 mapType = 'find'; 1811 1812 // Set container 1813 mapContainer = $('#interior-find'); 1814 if (typeof jsData !== 'undefined' && jsData.isMobile === 'true') { 1815 mapContainer.addClass('is-mobile'); 1816 } 1817 1818 // Set Panel 1819 mapPanel = mapContainer.find('.places-container'); 1820 1821 // Set templateDir 1822 templateDir = jsData.templateDir; 1823 if (typeof templateDir === 'undefined') { 1824 templateDir = ''; 1825 } 1826 1827 1828 latLng = initLatLng(); 1829 1830 // Set Options 1831 mapOptions = { 1832 zoom: 5, 1833 maxZoom: 18, 1834 minZoom: 5, 1835 center: latLng, 1836 backgroundColor: '#011f36', 1837 mapTypeId: google.maps.MapTypeId.ROADMAP, 1838 disableDefaultUI: false, 1839 scrollwheel: true, 1840 mapTypeControl: false, 1841 panControl: false, 1842 zoomControl: true, 1843 zoomControlOptions: { 1844 style: google.maps.ZoomControlStyle.DEFAULT, 1845 position: google.maps.ControlPosition.RIGHT_BOTTOM 1846 }, 1847 scaleControl: true, // fixed to BOTTOM_RIGHT 1848 streetViewControl: false, 1849 streetViewControlOptions: { 1850 position: google.maps.ControlPosition.RIGHT_TOP 1851 }, 1852 styles: mapStyles 1853 }; 1854 1855 // Initialize Map 1856 init(mapOptions); 1857 loading('Loading Properties...', true, '', $('#property-list-loader')); 1858 1859 } 1860 1861 function getPropertiesForMap() { 1862 $.ajax({ 1863 url: jsData.ajaxURL, 1864 type: 'POST', 1865 data: { 1866 action: 'get_properties_for_map' 1867 }, 1868 dataType: 'json', 1869 async: true 1870 }) 1871 .success(function (response) { 1872 if (!response.error) { 1873 // Build Markers/Places 1874 buildMarkersCustom(response); 1875 loading('', '', true, $('#property-list-loader')); 1876 1877 } 1878 }) 1879 .fail(function () { 1880 }); 1881 } 1882 1883 // LatLng 1884 function initLatLng(lat, lng) { 1885 // const { Map } = await google.maps.importLibrary("maps"); 1886 // Geocoding converts a string address into coordiantes (lat/lng) 1887 geocoder = new google.maps.Geocoder(); 1888 1889 lat = lat; 1890 if (typeof lat === 'undefined') { 1891 lat = ''; 1892 } 1893 1894 lng = lng; 1895 if (typeof lng === 'undefined') { 1896 lng = ''; 1897 } 1898 1899 var _latLng; 1900 1901 // Set intital position 1902 if (lat !== '' && lng !== '') { 1903 _latLng = new google.maps.LatLng(lat, lng); 1904 } else { 1905 // North America 1906 _latLng = new google.maps.LatLng(37.09024, -95.712891); 1907 } 1908 1909 return _latLng; 1910 1911 } 1912 1913 // Map Container 1914 function openMap(callback) { 1915 // Add class open 1916 mapContainer 1917 .addClass('open') 1918 .one(whichTransitionEvent(), function (event) { 1919 1920 if (mapContainer.hasClass('loaded')) {
1921 openPanel(); 1922 } 1923 1924 // Optional callback function 1925 if (typeof callback === 'function' && callback()) { 1926 callback(); 1927 } 1928 1929 }); 1930 } 1931 1932 function mapLoaded() { 1933 isLoaded = true; 1934 } 1935 1936 function isMapLoaded() { 1937 return isLoaded; 1938 } 1939 1940 function closeMap(callback) { 1941 closePanel(function () { 1942 mapContainer 1943 .removeClass('open') 1944 .one(whichTransitionEvent(), function (event) { 1945 1946 // Optional callback function 1947 if (typeof callback === 'function' && callback()) { 1948 callback(); 1949 } 1950 }); 1951 }); 1952 } 1953 1954 // Panel 1955 function openPanel(callback) { 1956 1957 var $placesList = mapPanel.find('.places-list'); 1958 1959 mapPanel 1960 .addClass('open') 1961 .one(whichTransitionEvent(), function (event) { 1962 1963 // Add ready to places list 1964 $placesList.addClass('ready'); 1965 1966 // Optional callback function 1967 if (typeof callback === 'function' && callback()) { 1968 callback(); 1969 } 1970 1971 }); 1972 1973 updateListHeight(); 1974 1975 // Sometimes TransitionEnd fails when cached 1976 // hacky solution to display content if it fails 1977 setTimeout(function () { 1978 if (!$placesList.hasClass('ready')) { 1979 $placesList.addClass('ready'); 1980 updateListHeight(); 1981 console.log('transition end failed'); 1982 } 1983 }, 1000); 1984 1985 } 1986 1987 function cloneGoogleLogo() { 1988 1989 setTimeout(function () { 1990 1991 var logo = $('a[title="Click to see this area on Google Maps"]').closest('div').clone(); 1992 1993 if (logo.length !== 0) { 1994 1995 if (logo.find('img').attr('src').indexOf('transparent.png') === -1) { 1996 $('[title="Click to see this area on Google Maps"]').hide(); 1997 logo.addClass('google-logo').appendTo(mapPanel); 1998 } 1999 2000 } 2001 2002 }, 1000); 2003 2004 } 2005 2006 function closePanel(callback) { 2007 2008 mapPanel 2009 .removeClass('open') 2010 .one(whichTransitionEvent(), function (event) { 2011 2012 // Optional callback function 2013 if (typeof callback === 'function' && callback()) { 2014 callback(); 2015 } 2016 2017 }); 2018 2019 } 2020 2021 function updateMapHeight() { 2022 2023 var windowBottom = $(window).height(); 2024 2025 var $pageHeader = $('.default-navigation'); 2026 2027 var $map = $('#interior-find'); 2028 2029 var $pageFooter = $('#footer'); 2030 2031 var newHeight = windowBottom - ($map.offset().top - $(window).scrollTop()) - $pageFooter.outerHeight(); 2032 2033 $map.height(newHeight); 2034 2035 } 2036 2037 function updateListHeight() { 2038 2039 var windowBottom = $(window).height(); 2040 2041 var list = ''; 2042 var bottomOffset = 50; 2043 if (mapType === 'explore') { 2044 2045 list = $('#interior-explore .places-list'); 2046 2047 } else if (mapType === 'find') { 2048 2049 list = $('#interior-find .places-list'); 2050 2051 if (mapContainer.hasClass('is-mobile')) { 2052 bottomOffset = $('#footer').outerHeight(); 2053 } else { 2054 bottomOffset = $('#footer').outerHeight() + 35; 2055 } 2056 } 2057 2058 var newHeight = windowBottom - (list.offset().top - $(window).scrollTop()) - bottomOffset; 2059 2060 list.height(newHeight); 2061 2062 if ($('#interior-explore .scroll-container').length) { 2063 2064 var details = $('#interior-explore .scroll-container'); 2065 2066 var alsoNewHeight = windowBottom - (details.offset().top - $(window).scrollTop()) - bottomOffset; 2067 2068 details.height(alsoNewHeight); 2069 } 2070 2071 } 2072 2073 $(window).on('resize', function () { 2074 2075 clearTimeout(resizeTimer); 2076 resizeTimer = setTimeout(function () { 2077 2078 // Update height of places list 2079 if (typeof mapContainer !== 'undefined') { 2080 if (mapType === 'explore') { 2081 2082 } else if (mapType === 'find') { 2083 updateMapHeight(); 2084 } 2085 2086 if (mapPanel.hasClass('open')) { 2087 updateListHeight(); 2088 } 2089 centerOnMarker(); 2090 } 2091 2092 }, 100); 2093 2094 }); 2095 2096 // Initialize/Reinit 2097 async function init(mapOptions) { 2098 const { Map } = await google.maps.importLibrary("maps"); 2099 // Create map 2100 map = new google.maps.Map($('.map-canvas')[0], mapOptions); 2101 2102 // Service we're using 2103 currentService = new google.maps.places.PlacesService(map); 2104 2105 // Update loading message 2106 loading('Initializing...'); 2107 2108 // Event listener - idle singles the map is loaded 2109 google.maps.event.addListenerOnce(map, 'idle', function () { 2110 2111 loading('Loading Map...'); 2112 2113 if (mapType === 'explore') { 2114 2115 setMainMarker(function () { 2116 2117 // Initial query 2118 searchMap('', 'general', 'distance'); 2119 2120 }); 2121 2122 } else if (mapType === 'find') { 2123 2124 // Get properties for map 2125 getPropertiesForMap(); 2126 2127 } 2128 2129 }); 2130 2131 // Event listener - tilesloaded signals that the maps fully loaded 2132 google.maps.event.addListenerOnce(map, 'tilesloaded', function () { 2133 2134 // Trigger mapLoaded event 2135 $(document).trigger('mapLoaded'); 2136 2137 }); 2138 2139 // Event listener - Restrict Panning 2140 var allowedBounds = new google.maps.LatLngBounds( 2141 new google.maps.LatLng(28.70, -127.50), 2142 new google.maps.LatLng(48.85, -55.90) 2143 ); 2144 2145 var lastValidCenter = map.getCenter(); 2146 2147 google.maps.event.addListener(map, 'center_changed', function () { 2148 if (allowedBounds.contains(map.getCenter())) { 2149 // still within valid bounds, so save the last valid position
2150 lastValidCenter = map.getCenter(); 2151 currentCenter = lastValidCenter; 2152 return; 2153 } 2154 2155 // not valid anymore then return to last valid position 2156 map.panTo(lastValidCenter); 2157 }); 2158 2159 // Event listener - On zoom change 2160 google.maps.event.addListener(map, 'zoom_changed', function () { 2161 if (mapType === 'find') { 2162 updateMarkerIcons(); 2163 } 2164 }); 2165 2166 // Create global icons 2167 tinyIcon = { 2168 url: templateDir + '/images/site/map-icon-tiny.png', 2169 size: new google.maps.Size(8, 8), 2170 anchor: new google.maps.Point(4, 4) 2171 }; 2172 2173 defaultIcon = { 2174 url: templateDir + '/images/site/map-icon-dot.png', 2175 size: new google.maps.Size(17, 16), 2176 anchor: new google.maps.Point(8, 8) 2177 }; 2178 2179 selectedIcon = { 2180 url: templateDir + '/images/site/map-icon-place.png', 2181 size: new google.maps.Size(30, 56), 2182 anchor: new google.maps.Point(15, 47) 2183 }; 2184 2185 defaultPropertyIcon = { 2186 url: templateDir + '/images/site/map-icon-cobblestone-default.png', 2187 size: new google.maps.Size(54, 84), 2188 anchor: new google.maps.Point(27, 72) 2189 }; 2190 2191 propertyIcon = { 2192 url: templateDir + '/images/site/map-icon-cobblestone.png', 2193 size: new google.maps.Size(54, 84), 2194 anchor: new google.maps.Point(27, 72) 2195 }; 2196 2197 } 2198 2199 function reinit() { 2200 // redraw map 2201 google.maps.event.trigger(map, 'resize'); 2202 2203 if (mapType === 'explore') { 2204 2205 // recenter 2206 centerOnMarker(); 2207 2208 } else if (mapType === 'find') { 2209 2210 } 2211 } 2212 2213 function offsetCenter(marker) { 2214 2215 // latlng is the apparent centre-point 2216 // offsetx is the distance you want that point to move to the right, in pixels 2217 // offsety is the distance you want that point to move upwards, in pixels 2218 // offset can be negative 2219 // offsetx and offsety are both optional 2220 2221 var markerLatlng; 2222 var mapLatlng = map.getCenter(); 2223 if (typeof (marker) === 'undefined' && typeof (selectedMarker) === 'undefined') { 2224 markerLatlng = map.getCenter(); 2225 } else if (typeof (marker) === 'undefined' && typeof (selectedMarker) !== 'undefined') { 2226 markerLatlng = selectedMarker.position; 2227 } else { 2228 markerLatlng = marker.position; 2229 } 2230 2231 var scale = Math.pow(2, map.getZoom()); 2232 var nw = new google.maps.LatLng( 2233 map.getBounds().getNorthEast().lat(),
2234 map.getBounds().getSouthWest().lng() 2235 ); 2236 2237 var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw); 2238 var worldCoordinate = map.getProjection().fromLatLngToPoint(markerLatlng); 2239 var pixelOffset = new google.maps.Point( 2240 Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), 2241 Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale) 2242 ); 2243 2244 var infoWidth = 200; 2245 2246 var offsetx = 0; 2247 // pixelOffset.x is the distance from the left side of the window 2248 // we -200 to compensate for when the infowindow is open 2249 if ((pixelOffset.x - infoWidth) < mapPanel.outerWidth()) { 2250 2251 // Below we get the offset - the width of the panel, this should give us the amount over or under the overlay. 2252 offsetx = (pixelOffset.x - mapPanel.outerWidth()); 2253 // If offsetx is less than 0, multiply it by -1 otherwise leave it. We want a positive number though. 2254 offsetx = offsetx < 0 ? offsetx * -1 : offsetx; 2255 // Add 200 to the offset to accommidate the infowindow 2256 offsetx += infoWidth; 2257 2258 // If the pixelOffset is bigger than the window 2259 } else if (pixelOffset.x > $(window).innerWidth()) { 2260 2261 // Subtract the window size from the offset, add the panel width 2262 offsetx = (pixelOffset.x - $(window).innerWidth()) + (mapPanel.outerWidth()); 2263 // subtract the infowindow margin 2264 offsetx -= infoWidth; 2265 // We want a negative number to move the center left 2266 offsetx = offsetx < 0 ? offsetx : offsetx * -1; 2267 } 2268 2269 var infoHeight = 250; 2270 2271 var offsety = 0; 2272 if (pixelOffset.y < infoHeight) { 2273 offsety = (infoHeight * -1) + pixelOffset.y; 2274 } else if (pixelOffset.y > $(window).height()) { 2275 offsety = (pixelOffset.y - $(window).innerHeight()) + infoHeight * 0.5; 2276 offsety = offsety * 1; 2277 } 2278 2279 var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(mapLatlng); 2280 var newOffset = new google.maps.Point((offsetx / scale), (offsety / scale)); 2281 2282 var worldCoordinateNewCenter = new google.maps.Point( 2283 worldCoordinateCenter.x - newOffset.x, 2284 worldCoordinateCenter.y + newOffset.y 2285 ); 2286 2287 var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter); 2288 2289 map.setCenter(newCenter); 2290 } 2291 2292 function offsetMap() { 2293 2294 var overlayWidth = mapPanel.outerWidth(); // Width of the overlay DIV 2295 var leftMargin = 25; // Grace margin to avoid too close fits on the edge of the overlay 2296 var rightMargin = 25; // Grace margin to avoid too close fits on the right and leave space for the controls 2297 2298 if (typeof bounds !== 'undefined' && bounds !== false) { 2299 2300 // Top right corner 2301 var topRightCorner = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getNorthEast().lng()); 2302 2303 // Top right point 2304 var topRightPoint = fromLatLngToPoint(topRightCorner).x; 2305 2306 // Get pixel position of leftmost and rightmost points
2307 var leftCoords = bounds.getSouthWest(); 2308 var leftMost = fromLatLngToPoint(leftCoords).x; 2309 var rightMost = fromLatLngToPoint(bounds.getNorthEast()).x; 2310 2311 // Calculate left and right offsets 2312 var leftOffset = (overlayWidth - leftMost); 2313 var rightOffset = ((topRightPoint - rightMargin) - rightMost); 2314 2315 // Only if left offset is needed 2316 if (leftOffset >= 0) { 2317 2318 if (leftOffset < rightOffset) { 2319 2320 var mapOffset = Math.round((rightOffset - leftOffset) / 2); 2321 2322 // Pan the map by the offset calculated on the x axis 2323 map.panBy(-mapOffset, 0); 2324 2325 // Get the new left point after pan 2326 var newLeftPoint = fromLatLngToPoint(leftCoords).x; 2327 2328 if (newLeftPoint <= overlayWidth) { 2329 2330 // Leftmost point is still under the overlay 2331 // Offset map again 2332 offsetMap(); 2333 } 2334 2335 } else { 2336 2337 // Cannot offset map at this zoom level otherwise both leftmost and rightmost points will not fit 2338 // Zoom out and offset map again 2339 map.setZoom(map.getZoom() - 1); 2340 offsetMap(); 2341 } 2342 } 2343 } 2344 } 2345 2346 function fromLatLngToPoint(latLng, offsetx, offsety) { 2347 2348 var scale = Math.pow(2, map.getZoom()); 2349 var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest
2349().lng()); 2350 var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw); 2351 var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng); 2352 2353 var pixelOffset = new google.maps.Point((offsetx / scale) || 0, (offsety / scale) || 0); 2354 2355 var worldCoordinateNewCenter = new google.maps.Point( 2356 Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale) - pixelOffset.x, 2357 Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale) + pixelOffset.y 2358 ); 2359 2360 return worldCoordinateNewCenter; 2361 } 2362 2363 function fitAndOffsetMap() { 2364 2365 if (bounds !== false) { 2366 2367 map.fitBounds(bounds); 2368 offsetMap(); 2369 } 2370 } 2371 2372 // Search - This function accepts several optional parameters to help customize the search 2373 // keywords expects a string 2374 // types can be a string or an array 2375 // blockTypes can be an array or a boolean (indicating weather or not to use the default) 2376 function searchMap(keywords, types, rankBy, blockTypes) { 2377 2378 // keywords = keywords; 2379 if (typeof (keywords) === 'undefined') { 2380 keywords = ''; 2381 } 2382 2383 // console.log(types); 2384 types = types; 2385 2386 if (keywords === '' && typeof (types) === 'undefined') { 2387 types = ['point_of_interest']; 2388 } 2389 2390 //rankBy = rankBy; 2391 if (typeof (rankBy) === 'undefined') { 2392 rankBy = 'distance'; 2393 } 2394 2395 // Places request 2396 var request = { 2397 location: propertyMarker.position, 2398 }; 2399 2400 if (rankBy === 'distance') { 2401 request.rankBy = google.maps.places.RankBy.DISTANCE; 2402 2403 } else if (rankBy === 'prominence') { 2404 request.rankBy = google.maps.places.RankBy.PROMINENCE; 2405 // this radius stipulation led to an error in combination with another bug 2406 request.radius = 30000; 2407 } 2408 2409 //this was altered to fix an issue with places API 7.27.2025 2410 if (keywords !== '') { 2411 request.keyword = keywords; 2412 } else { 2413 request.types = [types]; 2414 } 2415 2416 // Clear places list and overlays (the true tells it to also clear the array of markers) 2417 clearPlacesList(); 2418 clearOverlays(true); 2419 clearInfos(); 2420 2421 // For Google Places API Bug 2422 placesListCheck.length = 0; 2423 2424 console.log(request); 2425 2426 // Send request 2427 currentService.nearbySearch(request, buildMarkers(true, blockTypes)); 2428 2429 } 2430 2431 // Marker 2432 function setMainMarker(callback) { 2433 // Add new custom marker 2434 propertyMarker = new google.maps.Marker({ 2435 map: map, 2436 icon: propertyIcon, 2437 required: true, 2438 position: latLng 2439 }); 2440 2441 var content = getPropertyHTML(); 2442 2443 // Build property infowindow 2444 propertyMarker.info = new google.maps.InfoWindow({ 2445 content: content.clone()[0] 2446 }); 2447 2448 mapMarkers.push(propertyMarker); 2449 2450 // Bounce marker 2451 bounceMarker(propertyMarker, true); 2452 // Event listener - Waits for the creation of the infowindow html structure 2453 google.maps.event.addListener(propertyMarker.info, 'click', function () { 2454 //todo: this was DOMContentLoaded... but that does not exist in that listener 2455 infoWindowReady(); 2456 }); 2457 2458 // Event listener - Add event handler to current marker 2459 google.maps.event.addListener(propertyMarker, 'click', function () { 2460 clickPlaceMarker(this); 2461 }); 2462 2463 // Optional callback function 2464 if (typeof callback === 'function' && callback()) { 2465 callback(); 2466 } 2467 2468 } 2469 2470 function infoWindowReady() { 2471 $('.gm-style-iw').parent().hide().addClass('iw-custom').show(); 2472 } 2473 2474 function bounceMarker(marker, openInfo) { 2475 2476 // This function previously bounced the marker 2477 // It now just displays it
2478 2479 marker = marker; 2480 if (typeof marker === 'undefined') { 2481 marker = propertyMarker; 2482 } 2483 2484 openInfo = openInfo; 2485 if (typeof openInfo === 'undefined') { 2486 openInfo = false; 2487 } 2488 2489 marker.info.open(map, propertyMarker); 2490 2491 } 2492 2493 function centerOnMarker(marker) { 2494 marker = marker; 2495 if (typeof marker === 'undefined') { 2496 if (typeof (selectedMarker) !== 'undefined') { 2497 marker = selectedMarker; 2498 } else { 2499 marker = propertyMarker; 2500 } 2501 } 2502 offsetCenter(marker); 2503 //map.setCenter(marker.position); 2504 } 2505 2506 function fitMarkers(markers) { 2507 2508 markers = markers; 2509 if (typeof markers === 'undefined') { 2510 markers = mapMarkers; 2511 } 2512 var i = 0; 2513 for (i; i < markers.length; i++) { 2514 bounds.extend(markers[i].getPosition()); 2515 } 2516 bounds.extend(propertyMarker.position); 2517 2518 map.fitBounds(bounds); 2519 } 2520 2521 function clearOverlays(clearMarkerArray) { 2522 2523 // Optional param to not just clear from view, but also empty array 2524 clearMarkerArray = clearMarkerArray; 2525 if (typeof clearMarkerArray === 'undefined') { 2526 clearMarkerArray = false; 2527 } 2528 2529 clearMarkers(); 2530 //clearInfos(); 2531 2532 if (clearMarkerArray === true) { 2533 emptyMarkers(); 2534 } 2535 } 2536 2537 function clearMarkers() { 2538 if (mapMarkers.length > 0) { 2539 $.each(mapMarkers, function (i, marker) { 2540 if (marker.required !== true) { 2541 marker.setMap(null); 2542 } 2543 }); 2544 } 2545 } 2546 2547 function clearInfos() { 2548 if (mapMarkers.length >= 0) { 2549 $.each(mapMarkers, function (i, marker) { 2550 if (marker.info.getMap()) { 2551 marker.info.close(); 2552 } 2553 }); 2554 } 2555 2556 if (mapType === 'explore') { 2557 if (propertyMarker.info.getMap()) { 2558 propertyMarker.info.close(); 2559 } 2560 } 2561 } 2562 2563 function emptyMarkers() { 2564 mapMarkers = []; 2565 } 2566 2567 function buildMarkersCustom(data) { 2568 // Create list for panel 2569 // This function anticipates a big array of states and properties 2570 $.each(data, function (i, state) { 2571 $.each(state.info, function (abbr, fullState) { 2572 2573 // Main ul for the menu 2574 var ul = $('<ul>').attr({ 2575 class: 'state', 2576 id: cleanTitle(fullState) 2577 }); 2578 2579 // li for state title 2580 var name = $('<li>').attr({ 2581 class: 'state-title cobble-font', 2582 id: cleanTitle(fullState + '_title') 2583 }).text(fullState).appendTo(ul); 2584 2585 // Sub menu contains properties 2586 var subMenu = $('<ul>').attr({ 2587 class: 'sub-menu' 2588 }).appendTo(ul); 2589 2590 // Append State 2591 mapPanel.find('.places-list').append(ul); 2592 2593 $.each(state.properties, function (city, info) { 2594 createPlace(info); 2595 }); 2596 2597 document.getElementById(cleanTitle(cleanTitle(fullState) + '_title')).addEventListener('click', function () { 2598 if ($(this).closest('.state').find('.sub-menu:visible').length) { 2599 handleFindList(ul[0]); 2600 } else { 2601 var location = fullState + ', United States'; 2602 geocoder.geocode({'address': location}, function (results, status) { 2603 if (status === google.maps.GeocoderStatus.OK) { 2604 2605 map.setZoom(7); 2606 if (map.getCenter().equals(results[0].geometry.location)) { 2607 handleFindList(ul[0]); 2608 } else { 2609 map.setCenter(results[0].geometry.location); 2610 google.maps.event.addListenerOnce(map, 'idle', function () { 2611 handleFindList(ul[0]); 2612 }); 2613 } 2614 } else { 2615 console.log("Could not find location: " + location); 2616 } 2617 }); 2618 } 2619 }); 2620 }); 2621 });
2622 openPanel(updateMapHeight()); 2623 } 2624 2625 function handleFindList(target, closeCurrent) { 2626 2627 closeCurrent = (typeof closeCurrent === 'undefined') ? true : closeCurrent; 2628 2629 if (typeof target === 'string' || target instanceof String) { 2630 target = $('#find-a-cobblestone').find('#' + target); 2631 } else { 2632 target = $(target)[0]; 2633 if (target.id.indexOf('_marker') > -1) { 2634 target.id = target.id.replace('_marker', '_listing'); 2635 } 2636 } 2637 2638 if (target instanceof jQuery === false) { 2639 target = $('#' + target.id)[0]; 2640 } 2641 2642 //todo this is being thrown as the listener is not attached to the city button... 2643 var sameAsTarget = ($(target).closest('.state').attr('id') === $('#find-a-cobblestone ul').find('.sub-menu:visible').parent().attr('id')); 2644 2645 var subMenuOpen = ($(target).closest('.state').find('.sub-menu:visible').length) ? true : false; 2646 2647 if (closeCurrent || !sameAsTarget) { 2648 $('#find-a-cobblestone ul').find('.sub-menu:visible').stop().slideToggle(300); 2649 } 2650 2651 if (!sameAsTarget || subMenuOpen) { 2652 if (!subMenuOpen) { 2653 $(target).closest('.state').find('.sub-menu').stop().slideToggle(300, function () { 2654 var newTop = $('#find-a-cobblestone').scrollTop() - $('#find-a-cobblestone').offset().top + $(target).offset().top; 2655 $('#find-a-cobblestone').finish().animate({scrollTop: newTop}); 2656 }); 2657 } else { 2658 2659 var newTop = $('#find-a-cobblestone').scrollTop() - $('#find-a-cobblestone').offset().top + $(target).offset().top; 2660 $('#find-a-cobblestone').finish().animate({scrollTop: newTop}); 2661 } 2662 2663 } 2664 } 2665 2666 function buildMarkers(display, blockTypes) { 2667 2668 // Show search form loader 2669 showSearchLoader(); 2670 2671 blockTypes = blockTypes; 2672 if (typeof blockTypes === 'undefined') { 2673 blockTypes = ''; 2674 } 2675 2676 var initialDisplay = display; 2677 if (typeof initialDisplay === 'undefined') { 2678 initialDisplay = true; 2679 } 2680 2681 return function (results, status, pagination) { 2682 2683 if (status === google.maps.places.PlacesServiceStatus.OK) { 2684 2685 bounds = new google.maps.LatLngBounds(); 2686 2687 // Filter places 2688 var placesList = []; 2689 for (var index = 0; index < results.length; index++) { 2690 if (filterPlace(results[index], blockTypes) !== false) { 2691 // Google Places API bug with pagination fix 2692 // Check to see if place has already been listed 2693 if ($.inArray(results[index].place_id, placesListCheck) === -1) { 2694 placesListCheck.push(results[index].place_id); 2695 placesList.push(results[index]); 2696 } 2697 } 2698 } 2699 2700 // Check placesListCheck as well because of Google Places API Bug 2701 // Bascially, placesListCheck contains a list of all results and grows with pagination 2702 // The API is returning multiple lists (like page 2 and 3 when you get to page 3) 2703 // Then placesListCheck keeps track of what has already been displayed and removes anything it's 2704 // already seen. The problem is that, due to the bug, placesList will equal 0 when going from page 2 to 3 2705 // because the API sends page 2 again and then page 3. Since page 2 has been seen already it thinks there are no 2706 // results, so it flickers "no results" before getting to page 3. 2707 if (placesList.length === 0 && placesListCheck.length === 0) { 2708 2709 showNoResults(); 2710 2711 } else { 2712 2713 // Hide no results 2714 showNoResults(false); 2715 2716 bounds.extend(propertyMarker.position); 2717 2718 // and create new markers by search result 2719 for (var i = 0; i < placesList.length; i++) { 2720 createPlace(placesList[i]); 2721 bounds.extend(placesList[i].geometry.location); 2722 } 2723 2724 // Handle pagination
2725 var showMore = $('#interior-explore .show-more'); 2726 if (pagination.hasNextPage) { 2727 // There are more results 2728 hasMoreResults(); 2729 2730 // show more button click 2731 // google.maps.event.addDomListener(showMore[0], 'click', function(){ 2732 // showSearchLoader(); 2733 // pagination.nextPage(); 2734 // }); 2735 2736 // show more button click 2737 window.addEventListener('DOMContentLoaded', function () { 2738 //todo: used showMore[0] here 2739 showSearchLoader(); 2740 pagination.nextPage(); 2741 }); 2742 } else { 2743 hasMoreResults(false); 2744 } 2745 2746 if (initialSearch) { 2747 // For initial search, center on the property 2748 centerOnMarker(); 2749 initialSearch = false; 2750 } else { 2751 // Offset map 2752 fitAndOffsetMap(); 2753 } 2754 2755 } 2756 2757 } else if (status === google.maps.places.PlacesServiceStatus.ZERO_RESULTS) { 2758 2759 // Show no results 2760 showNoResults(); 2761 } 2762 2763 // Hide search loader 2764 showSearchLoader(false); 2765 2766 }; 2767 } 2768 2769 function filterPlace(obj, blockTypes) { 2770 2771 // Don't filter lodging if it includes campground 2772 2773 blockTypes = blockTypes; 2774 if (typeof blockTypes === 'undefined' || blockTypes === '') { 2775 blockTypes = ['lodging']; 2776 } 2777 2778 // This is so that things listed as lodging and campground are included 2779 var safeTypes = ['campground']; 2780 2781 var isValid = true; 2782 var isSafe = false; 2783 $(obj.types).each(function (i, e) { 2784 if ($.inArray(e, blockTypes) > -1 || obj.permanently_closed) { 2785 isValid = false; 2786 } 2787 if ($.inArray(e, safeTypes) > -1) { 2788 isSafe = true; 2789 } 2790 }); 2791 2792 if (isSafe && !isValid) { 2793 isValid = true; 2794 } 2795 2796 return isValid; 2797 2798 } 2799 2800 function hasMoreResults(trueFalse) { 2801 2802 trueFalse = trueFalse; 2803 if (typeof trueFalse === 'undefined') { 2804 trueFalse = true; 2805 } 2806 2807 var $showMore = $('#interior-explore .show-more'); 2808 2809 if (trueFalse === false) { 2810 $showMore.hide(); 2811 } else { 2812 $showMore.show(); 2813 } 2814 2815 } 2816 2817 function showNoResults(trueFalse) { 2818 2819 trueFalse = trueFalse; 2820 if (typeof trueFalse === 'undefined') { 2821 trueFalse = true; 2822 } 2823 2824 var $noResults = mapPanel.find('.no-results'); 2825 2826 if (trueFalse === false) { 2827 $noResults.hide(); 2828 } else { 2829 $noResults.show(); 2830 hasMoreResults(false); 2831 } 2832 2833 } 2834 2835 function showSearchLoader(trueFalse) { 2836 2837 trueFalse = trueFalse; 2838 if (typeof trueFalse === 'undefined') { 2839 trueFalse = true; 2840 } 2841 2842 var $loader = $('#search-form .submit-button .loader'); 2843 2844 if (trueFalse === false) { 2845 $loader.hide(); 2846 } else { 2847 $loader.css('display', 'inline-block'); 2848 } 2849 2850 } 2851 2852 function createPlace(place) { 2853 var marker; 2854 var li; 2855 if (mapType === 'explore') { 2856 // Get content for info window wrapped in HTML tags 2857 var content = getPlaceHTML(place); 2858 2859 // Prepare new marker object 2860 marker = new google.maps.Marker({ 2861 position: place.geometry.location, 2862 map: map, 2863 title: place.name, 2864 icon: defaultIcon, 2865 id: place.place_id + '_marker', 2866 }); 2867 2868 // Prepare new infowindow 2869 marker.info = new google.maps.InfoWindow({ 2870 content: content.clone().removeAttr('id')[0] 2871 }); 2872 2873 // Add to markers array 2874 mapMarkers.push(marker); 2875 2876 // Create Listing 2877 li = $('<li>').attr({ 2878 class: 'place' 2879 }).html(content).appendTo(mapPanel.find('.places-list ul')); 2880 2881 } else if (mapType === 'find') { 2882 // Create propertyList 2883 2884 // Get content for info window wrapped in HTML tags 2885 var markerContent = getPropertyHTML(place); 2886 2887 // Get content for list 2888 var listContent = getPropertyListHTML(place); 2889 2890 // Prepare new marker object 2891 marker = new google.maps.Marker({ 2892 position: initLatLng(place.latitude, place.longitude), 2893 map: map, 2894 title: place.city, 2895 icon: tinyIcon,
2896 id: cleanTitle(place.city) + '_' + cleanTitle(place.state_abbr) + '_marker', 2897 state: cleanTitle(place.state), 2898 required: true 2899 }); 2900 2901 zoomLevelDefaultIcon = marker.getIcon(); 2902 2903 // Prepare new infowindow 2904 marker.info = new google.maps.InfoWindow({ 2905 content: markerContent[0] 2906 }); 2907 2908 // Add to markers array 2909 mapMarkers.push(marker); 2910 2911 // Create Listing 2912 li = $('<li>').attr({ 2913 class: 'place' 2914 }).html(listContent).appendTo(mapPanel.find('.places-list .state:last-child .sub-menu')); 2915 } 2916 2917 if (typeof (marker) !== 'undefined') { 2918 //used in the third listener.. 2919 // let markerName = cleanTitle(place.city) + '_' + cleanTitle(place.state_abbr) + '_marker'; 2920 2921 // Event listener - Waits for the creation of the infowindow html structure 2922 google.maps.event.addListener(marker.info, 'DOMContentLoaded', function () { 2923 infoWindowReady(); 2924 }); 2925 2926 // Event listener - Add event handler to current marker 2927 google.maps.event.addListener(marker, 'click', function () { 2928 clickPlaceMarker(this); 2929 }); 2930 2931 //todo: replacement 2932 //the object is not loaded yet.... how to wait? 2933 // document.getElementById(markerName).addEventListener('click', function() { 2934 // if(!working && !$(event.target).is('a')){ 2935 // working = true; 2936 // clickPlaceList(this); 2937 // } 2938 // }); 2939 2940 // Event listener - Add event handler for clicking on place in list 2941 // google.maps.event.addDomListener(li[0], 'click', function(event) { 2942 // if(!working && !$(event.target).is('a')){ 2943 // working = true; 2944 // clickPlaceList(this); 2945 // } 2946 // }); 2947 2948 //todo split by map type 2949 if (mapType === 'find') { 2950 document.getElementById(cleanTitle(place.city) + '_' + cleanTitle(place.state_abbr) + '_listing').addEventListener('click', function () { 2951 //todo update this "event" call below 2952 if (!working && !$(event.target).is('a')) { 2953 working = true; 2954 clickPlaceList(place, cleanTitle(place.city) + '_' + cleanTitle(place.state_abbr) + '_marker'); 2955 } 2956 }); 2957 } else { 2958 document.getElementById(place.place_id + '_listing').addEventListener('click', function () { 2959 //todo update this "event" call below 2960 if (!working && !$(event.target).is('a')) { 2961 working = true; 2962 clickPlaceList(place, place.place_id + '_marker'); 2963 } 2964 }); 2965 } 2966 } 2967 2968 } 2969 2970 function updateCurrentMarker(marker, callback) { 2971 2972 if (mapType === 'explore') { 2973 2974 if (selectedMarker) { 2975 selectedMarker.setIcon(defaultIcon); 2976 } 2977 2978 if (marker.required !== true) { 2979 marker.setIcon(selectedIcon); 2980 selectedMarker = marker; 2981 } 2982 2983 } else if (mapType === 'find') { 2984 2985 if (selectedMarker) { 2986 selectedMarker.setIcon(zoomLevelDefaultIcon); 2987 } 2988 2989 marker.setIcon(propertyIcon); 2990 selectedMarker = marker; 2991 2992 } 2993 2994 // Optional callback function 2995 if (typeof callback === 'function' && callback()) { 2996 callback(); 2997 } 2998 2999 } 3000 3001 function updateMarkerIcons() { 3002 3003 var zoomLevel = map.getZoom(); 3004 $.each(mapMarkers, function (i, v) { 3005 if (v !== selectedMarker) { 3006 if (zoomLevel <= 5) { 3007 v.setIcon(tinyIcon); 3008 } else if (zoomLevel <= 6) { 3009 v.setIcon(defaultIcon); 3010 } else { 3011 v.setIcon(defaultPropertyIcon); 3012 } 3013 } 3014 zoomLevelDefaultIcon = v.getIcon(); 3015 }); 3016 3017 } 3018 3019 function clickPlaceMarker(marker) { 3020 clearInfos(); 3021 var m = marker.getMap(); 3022 if (mapType === 'explore') { 3023 closeDetails(); 3024 } else if (mapType === 'find') { 3025 handleFindList(marker, false); 3026 } 3027 3028 updateCurrentMarker(marker, setTimeout(function () { 3029 marker.info.open(m, marker); 3030 }, 400)); 3031 } 3032 3033 function clickPlaceList(place, mapTarget) { 3034 clearInfos(); 3035 let id = place.id; 3036 3037 if (typeof id === 'undefined') { 3038 id = place.place_id; 3039 } 3040 3041 if (typeof mapTarget !== "undefined" && mapTarget !== null) { 3042 3043 //he is looping through each marker to find the one we want? ok.. but... we know
3043which one we want. 3044 $.each(mapMarkers, function (i, marker) { 3045 3046 //markers simplified 3047 // if (id.replace('_listing', '_marker') === marker.id || id + '_marker' === marker.id) { 3048 if (mapTarget === marker.id) { 3049 3050 var m = marker.getMap(); 3051 3052 if (mapType === 'explore') { 3053 openDetails(id); 3054 } else if (mapType === 'find') { 3055 //updateCurrentMarker(marker); 3056 working = false; 3057 } 3058 3059 updateCurrentMarker(marker, function () { 3060 centerOnMarker(marker); 3061 marker.info.open(m, marker); 3062 }); 3063 3064 return; 3065 } 3066 }); 3067 } 3068 } 3069 3070 function filterIconUrl(url) { 3071 3072 var types = [ 3073 'airport', 3074 'aquarium', 3075 'art_gallery', 3076 'bar', 3077 'barber', 3078 'bowling', 3079 'cafe', 3080 'camping', 3081 'car_dealer', 3082 'casino', 3083 'civic_building', 3084 'dentist', 3085 'doctor', 3086 'electronics', 3087 'fitness', 3088 'gas_station', 3089 'generic_business', 3090 'generic_recreational', 3091 'government', 3092 'lodging', 3093 'museum', 3094 'repair', 3095 'restaurant', 3096 'shopping', 3097 'stadium', 3098 'travel_agent', 3099 'worship_general' 3100 ]; 3101 3102 var t = ''; 3103 $.each(types, function (i, type) { 3104 if (url.indexOf(type) !== -1) { 3105 t = type; 3106 } 3107 }); 3108 3109 return t; 3110 3111 } 3112 3113 function getIcon(url) { 3114 3115 var type = filterIconUrl(url); 3116 3117 var iconUrl = templateDir; 3118 3119 switch (type) { 3120 3121 case 'airport': 3122 iconUrl += '/images/site/map-icons/airport-71.png'; 3123 break; 3124 3125 case 'aquarium': 3126 iconUrl += '/images/site/map-icons/aquarium-71.png'; 3127 break; 3128 3129 case 'art_gallery': 3130 iconUrl += '/images/site/map-icons/art_gallery-71.png'; 3131 break; 3132 3133 case 'bar': 3134 iconUrl += '/images/site/map-icons/bar-71.png'; 3135 break; 3136 3137 case 'barber': 3138 iconUrl += '/images/site/map-icons/barber-71.png'; 3139 break; 3140 3141 case 'bowling': 3142 iconUrl += '/images/site/map-icons/bowling-71.png'; 3143 break; 3144 3145 case 'cafe': 3146 iconUrl += '/images/site/map-icons/cafe-71.png'; 3147 break; 3148 3149 case 'camping': 3150 iconUrl += '/images/site/map-icons/camping-71.png'; 3151 break; 3152 3153 case 'car_dealer': 3154 iconUrl += '/images/site/map-icons/car_dealer-71.png'; 3155 break; 3156 3157 case 'casino': 3158 iconUrl += '/images/site/map-icons/casino-71.png'; 3159 break; 3160 3161 case 'civic_building': 3162 iconUrl += '/images/site/map-icons/civic_building-71.png'; 3163 break; 3164 3165 case 'dentist': 3166 iconUrl += '/images/site/map-icons/dentist-71.png'; 3167 break; 3168 3169 case 'doctor': 3170 iconUrl += '/images/site/map-icons/doctor-71.png'; 3171 break; 3172 3173 case 'electronics': 3174 iconUrl += '/images/site/map-icons/electronics-71.png'; 3175 break; 3176 3177 case 'fitness': 3178 iconUrl += '/images/site/map-icons/fitness-71.png'; 3179 break; 3180 3181 case 'gas_station': 3182 iconUrl += '/images/site/map-icons/gas_station-71.png'; 3183 break; 3184 3185 case 'generic_business': 3186 iconUrl += '/images/site/map-icons/generic_business-71.png'; 3187 break; 3188 3189 case 'generic_recreational': 3190 iconUrl += '/images/site/map-icons/generic_recreational-71.png'; 3191 break; 3192 3193 case 'government': 3194 iconUrl += '/images/site/map-icons/government-71.png'; 3195 break; 3196 3197 case 'lodging': 3198 iconUrl += '/images/site/map-icons/lodging-71.png'; 3199 break; 3200 3201 case 'museum': 3202 iconUrl += '/images/site/map-icons/museum-71.png'; 3203 break; 3204 3205 case 'repair': 3206 iconUrl += '/images/site/map-icons/repair-71.png'; 3207 break; 3208 3209 case 'restaurant': 3210 iconUrl += '/images/site/map-icons/restaurant-71.png'; 3211 break; 3212 3213 case 'shopping': 3214 iconUrl += '/images/site/map-icons/shopping-71.png'; 3215 break; 3216 3217 case 'stadium': 3218 iconUrl += '/images/site/map-icons/stadium-71.png'; 3219 break; 3220 3221 case 'travel_agent': 3222 iconUrl += '/images/site/map-icons/travel_agent-71.png'; 3223 break; 3224 3225 case 'worship_general': 3226 iconUrl += '/images/site/map-icons/worship_general-71.png'; 3227 break; 3228 3229 default: 3230 iconUrl += '/images/site/map-icons/generic_business-71.png'; 3231 3232 } 3233 3234 return iconUrl; 3235 3236 } 3237 3238 function clearPlacesList() { 3239 mapPanel.find('.places-list li').remove(); 3240 } 3241 3242 function getPropertyListHTML(data) { 3243 3244 // Create and return all HTML for the property/state list 3245 var id = data.id; 3246 var logo = data.alt_logo; 3247 var address = data.address; 3248 var city = data.city; 3249 var page = data.link; 3250 3251 // Content container 3252 var html = $('<div>').attr({ 3253 class: 'place-content property', 3254 }); 3255 3256 // Two columns 3257 var col1 = $('<div>').attr({ 3258 class: 'col-1' 3259 }); 3260
3261 var col2 = $('<div>').attr({ 3262 class: 'col-2' 3263 }); 3264 3265 // Create content html 3266 var div = $('<div>').attr({ 3267 class: 'logo', 3268 style: 'background-image: url(' + logo + ');' 3269 }).appendTo(col1); 3270 3271 // City 3272 var _city = $('<h3>').attr({ 3273 class: 'city', 3274 id: cleanTitle(data.city) + '_' + cleanTitle(data.state_abbr) + '_listing' 3275 }).text(city + ', ' + data.state_abbr).appendTo(col2); 3276 3277 // Address 3278 var _address = $('<span>').attr({ 3279 class: 'address' 3280 }).text(address).appendTo(col2); 3281 3282 // Check Page Link 3283 var _pageLink = $('<div>').attr({ 3284 class: 'page-link' 3285 }).appendTo(col2); 3286 3287 var _pageLinkLink = $('<a>').attr({ 3288 href: data.link, 3289 class: 'cobble-font track-visit-page' 3290 }).text('Visit Page').appendTo(_pageLink); 3291 3292 var _pageLinkIcon = $('<i>').attr({ 3293 class: 'fa fa-angle-right' 3294 }).appendTo(_pageLink); 3295 3296 3297 // Append columns to main container 3298 col1.appendTo(html); 3299 col2.appendTo(html); 3300 3301 return html; 3302 3303 3304 } 3305 3306 function getPropertyHTML(data) { 3307 3308 if (typeof data === 'undefined') { 3309 3310 data = $.extend({}, propertyData, metaData); 3311 } 3312 3313 var id = data.id; 3314 var logo = data.alt_logo; 3315 var type = data.type; 3316 var address = data.address_full; 3317 var bookingID = data.booking_id; 3318 var page = data.link; 3319 3320 // _phone will be the unformatted number 3321 var _phone = data.phone; 3322 var phone = data.phone_formatted; 3323 if (phone === '') { 3324 _phone = data.tollfree; 3325 phone = data.tollfree_formatted; 3326 } 3327 var email = fixEmail(data.email); 3328 var desc = data.brief_description; 3329 3330 // Content container 3331 var html = $('<div>').attr({ 3332 class: 'place-content property', 3333 id: id 3334 }); 3335 3336 // Two columns 3337 var col1 = $('<div>').attr({ 3338 class: 'col-1' 3339 }); 3340 3341 var col2 = $('<div>').attr({ 3342 class: 'col-2' 3343 }); 3344 3345 // Create content html 3346 if (logo !== '') { 3347 var div = $('<a>').attr({ 3348 class: 'logo track-logo-link', 3349 style: 'background-image: url(' + logo + ');', 3350 href: data.link 3351 }).appendTo(col1); 3352 } 3353 3354 // Desc 3355 if (desc !== '') { 3356 var d = $('<p>').attr({ 3357 class: 'desc', 3358 }).text(desc).appendTo(col2); 3359 } 3360 3361 // Address 3362 if (address !== '') { 3363 var span = $('<span>').attr({ 3364 class: 'address', 3365 }).text(address).appendTo(col2); 3366 } 3367 3368 // Phone 3369 var p = $('<a>').attr({ 3370 class: 'phone phone_number track-call', 3371 href: 'tel:' + _phone 3372 }).text(phone).appendTo(col1); 3373 3374 // Email 3375 var e = $('<a>').attr({ 3376 class: 'email track-email', 3377 href: 'mailto:' + email 3378 }).text('Send Email').appendTo(col1); 3379 3380 // Booking Link 3381 var b = $('<a>').attr({ 3382 class: 'book-now', 3383 href: '#' + bookingID 3384 }).text('Book Now').appendTo(col1); 3385 3386 // Append columns to main container 3387 col1.appendTo(html); 3388 col2.appendTo(html); 3389 3390 return html; 3391 } 3392 3393 function getPlaceHTML(place) { 3394 var icon = getIcon(place.icon); 3395 3396 var id = place.place_id; 3397 if (typeof id === 'undefined') { 3398 id = ''; 3399 } 3400 3401 var name = place.name; 3402 if (typeof name === 'undefined') { 3403 name = ''; 3404 } 3405 3406 var hours = place.opening_hours; 3407 if (typeof hours === 'undefined') { 3408 hours = ''; 3409 } 3410 // else { 3411 // var isOpen = place.opening_hours.isOpen(); 3412 // } 3413 // 3414 // console.log('isOpen = ' + isOpen); 3415 3416 var photos = place.photos; 3417 if (typeof photos === 'undefined') { 3418 photos = []; 3419 } 3420 3421 var vicinity = place.vicinity; 3422 if (typeof vicinity === 'undefined') { 3423 vicinity = ''; 3424 } 3425 3426 // Content container 3427 var html = $('<div>').attr({ 3428 class: 'place-content', 3429 id: id + '_listing' 3430 }); 3431 3432 // Create content html 3433 if (icon !== '') { 3434 var span = $('<span>').attr({ 3435 class: 'icon', 3436 style: 'background-image: url(' + icon + ');' 3437 }).appendTo(html); 3438 } 3439
3440 // Background style 3441 var photoStyle = ''; 3442 if (photos.length > 0) { 3443 3444 photoStyle = 'background-image: url(' + photos[0].getUrl({'maxWidth': 420, 'maxHeight': 300}) + ');'; 3445 3446 } else { 3447 photoStyle = 'background-image: url(' + templateDir + '/images/site/black-transparent-light.png);'; 3448 } 3449 3450 // Photo 3451 var div = $('<div>').attr({ 3452 class: 'photo', 3453 style: photoStyle 3454 }).appendTo(html); 3455 3456 // Place title 3457 var h4 = $('<h4>').attr({ 3458 class: 'cobble-font' 3459 }).text(name + ' ').appendTo(html); 3460 3461 // Distance 3462 var distance = $('<span>').attr({ 3463 class: 'distance' 3464 }).appendTo(h4); 3465 3466 // Vicinity/Address 3467 if (vicinity !== '') { 3468 var spanOne = $('<span>').attr({ 3469 class: 'address', 3470 }).text(vicinity).appendTo(html); 3471 } 3472 3473 // Open now 3474 if (typeof hours.open_now !== "undefined" && hours.open_now !== null) { 3475 var _class = ''; 3476 var _text = ''; 3477 if (hours.open_now === true) { 3478 _class = 'is-open'; 3479 _text = 'Open Now'; 3480 } else if (hours.open_now === false) { 3481 _class = 'is-closed'; 3482 _text = 'Currently Closed'; 3483 } 3484 var spanTwo = $('<span>').attr({ 3485 class: _class 3486 }).text(_text).appendTo(html); 3487 } 3488 3489 // Calculate distance 3490 calculateDistance(place); 3491 3492 return html; 3493 } 3494 3495 // Place Details 3496 function toggleDetails() { 3497 } 3498 3499 function clearDetails() { 3500 var loader = $('.place-details .loader').detach(); 3501 $('.place-details').empty().append(loader); 3502 } 3503 3504 function setDetailsCSS() { 3505 // Set padding for content under place detail image 3506 var imageHeight = 225 - mapPanel.find('.close-detail').outerHeight(); 3507 $('#interior-explore .place-details').css('margin-top', imageHeight + 'px'); 3508 } 3509 3510 function openDetails(id) { 3511 3512 // Clear previous details 3513 clearDetails(); 3514 3515 // Set any special CSS 3516 setDetailsCSS(); 3517 3518 // Show loader 3519 $('.place-details .loader').show(); 3520 3521 // Set place ID to be sent to google 3522 var placeId = { 3523 placeId: id 3524 }; 3525 3526 // Get details 3527 var service = new google.maps.places.PlacesService(map); 3528 service.getDetails(placeId, function (place, status) { 3529 if (status === google.maps.places.PlacesServiceStatus.OK) { 3530 3531 // Hide loader 3532 $('.place-details .loader').hide(); 3533 3534 // Place details container 3535 var container = $('.place-details'); 3536 3537 // Image/Alt Image 3538 var image = $('<div>').attr({ 3539 class: 'image' 3540 }).appendTo(container); 3541 3542 if (typeof place.photos !== 'undefined' && place.photos !== null) { 3543 var img = place.photos[0].getUrl({'maxWidth': 500, 'maxHeight': 222}); 3544 image.attr({ 3545 style: 'background-image: url(' + img + ')' 3546 }); 3547 } 3548 3549 // Title 3550 var title = $('<h4>').attr({ 3551 class: 'title cobble-font' 3552 }).text(place.name).appendTo(container); 3553
3554 // Type-Price-Open-Now Container 3555 var tpon = $('<div>').attr({ 3556 class: 'type-price-container' 3557 }); 3558 3559 // Type 3560 var type = $('<span>').attr({ 3561 class: 'type sub-title' 3562 }).text(place.types[0].replace(/[^a-zA-Z-]/g, ' ')).appendTo(tpon); 3563 3564 // Open Now 3565 if (typeof place.opening_hours !== "undefined" && place.opening_hours !== null) { 3566 3567 if (typeof place.opening_hours.isOpen() !== "undefined" && place.opening_hours.isOpen() !== null) { 3568 //todo: since isOpen is now a boolean, this statement is odd now 3569 var openNow = place.opening_hours.isOpen(); 3570 var _class = ''; 3571 var _text = ''; 3572 3573 if (openNow) { 3574 _class = 'is-open'; 3575 _text = 'Open Now'; 3576 3577 } else { 3578 _class = 'is-closed'; 3579 _text = 'Currently Closed'; 3580 } 3581 3582 var openContainer = $('<span>').attr({ 3583 class: 'sub-title ' + _class 3584 }).text(_text).appendTo(tpon); 3585 } 3586 } 3587 3588 // Price Level 3589 if (typeof place.price_level !== "undefined" && place.price_level !== null) { 3590 var priceLevel = place.price_level; 3591 var level; 3592 switch (priceLevel) { 3593 case 0: 3594 level = ''; 3595 break; 3596 case 1: 3597 level = '<i class="fa fa-dollar"></i>'; 3598 break; 3599 case 2: 3600 level = '<i class="fa fa-dollar"></i><i class="fa fa-dollar"></i>'; 3601 break; 3602 case 3: 3603 level = '<i class="fa fa-dollar"></i><i class="fa fa-dollar"></i><i class="fa fa-dollar"></i>'; 3604 break; 3605 case 4: 3606 level = '<i class="fa fa-dollar"></i><i class="fa fa-dollar"></i><i class="fa fa-dollar"></i><i class="fa fa-dollar"></i>'; 3607 break; 3608 default: 3609 level = ''; 3610 3611 } 3612 var price = $('<span>').attr({ 3613 class: 'price-level sub-title' 3614 }).html(level).appendTo(tpon); 3615 } 3616 tpon.appendTo(container); 3617 3618 // Elements with Icons
3619 var scrollContainer = $('<div>').attr({ 3620 class: 'scroll-container' 3621 }); 3622 3623 // Address 3624 if (typeof place.formatted_address !== "undefined" && place.formatted_address !== null) { 3625 // Container element 3626 var a = $('<div>').attr({ 3627 class: 'address-container' 3628 }); 3629 // Icon 3630 var ai = $('<i>').attr({ 3631 class: 'fa fa-map-marker' 3632 }); 3633 ai.appendTo(a); 3634 // Content 3635 var address = $('<span>').attr({ 3636 class: 'address', 3637 }).text(place.formatted_address).appendTo(a); 3638 a.appendTo(scrollContainer); 3639 } 3640 3641 // Phone 3642 if (typeof place.formatted_phone_number !== "undefined" && place.formatted_phone_number !== null) { 3643 // Container element 3644 var p = $('<div>').attr({ 3645 class: 'phone-container' 3646 }); 3647 // Icon 3648 var pi = $('<i>').attr({ 3649 class: 'fa fa-phone' 3650 }); 3651 pi.appendTo(p); 3652 // Content 3653 var number = place.formatted_phone_number.replace(/[^0-9.]/g, ''); 3654 var phone = $('<a>').attr({ 3655 class: 'phone phone_number', 3656 href: 'tel:' + number 3657 }).text(place.formatted_phone_number).appendTo(p); 3658 p.appendTo(scrollContainer); 3659 } 3660 3661 // Website 3662 if (typeof place.website !== "undefined" && place.website !== null) { 3663 // Container element 3664 var w = $('<div>').attr({ 3665 class: 'website-container' 3666 }); 3667 // Icon 3668 var wi = $('<i>').attr({ 3669 class: 'fa fa-globe' 3670 }); 3671 wi.appendTo(w); 3672 // Content 3673 var site = $('<a>').attr({ 3674 class: 'website', 3675 href: place.website, 3676 target: '_blank' 3677 }).text('Visit Website').appendTo(w); 3678 w.appendTo(scrollContainer); 3679 } 3680 3681 // Hours 3682 if (typeof place.opening_hours !== "undefined" && place.opening_hours !== null) { 3683 3684 if (typeof place.opening_hours.weekday_text !== "undefined" && place.opening_hours.weekday_text !== null) { 3685 // Container element 3686 var oh = $('<div>').attr({ 3687 class: 'hours-container' 3688 }); 3689 // Icon 3690 var ohi = $('<i>').attr({ 3691 class: 'fa fa-clock-o' 3692 }); 3693 ohi.appendTo(oh); 3694 // Content 3695 var hours = place.opening_hours.weekday_text; 3696 var ul = $('<ul>').attr({ 3697 class: 'hours' 3698 }).appendTo(oh); 3699 $.each(hours, function (i, e) { 3700 $('<li>').text(e).appendTo(ul); 3701 }); 3702 oh.appendTo(scrollContainer); 3703 } 3704 3705 } 3706 3707 scrollContainer.appendTo(container); 3708 3709 // Set class
3710 mapPanel.addClass('show-details') 3711 .find('.content-container') 3712 .one(whichTransitionEvent(), function (event) { 3713 mapPanel.addClass('ready'); 3714 updateListHeight(); 3715 }); 3716 3717 working = false; 3718 3719 } else { 3720 3721 updateListHeight(); 3722 3723 working = false; 3724 } 3725 3726 }); 3727 3728 } 3729 3730 function closeDetails() { 3731 mapPanel.removeClass('ready') 3732 .one(whichTransitionEvent(), function (event) { 3733 mapPanel.removeClass('show-details'); 3734 clearDetails(); 3735 }); 3736 updateListHeight(); 3737 } 3738 3739 function calculateDistance(place) { 3740 var service = new google.maps.DistanceMatrixService(); 3741 return service.getDistanceMatrix({ 3742 origins: [propertyMarker.position], 3743 destinations: [place.geometry.location], 3744 travelMode: google.maps.TravelMode.DRIVING, 3745 unitSystem: google.maps.UnitSystem.IMPERIAL, 3746 avoidHighways: false, 3747 avoidTolls: false 3748 }, displayDistance(place.place_id)); 3749 } 3750 3751 function displayDistance(id) { 3752 return function (response, status) { 3753 if (status !== google.maps.DistanceMatrixStatus.OK) { 3754 console.log('Error was: ' + status); 3755 } else { 3756 var origins = response.originAddresses; 3757 var destinations = response.destinationAddresses; 3758 for (var i = 0; i < origins.length; i++) { 3759 var results = response.rows[i].elements; 3760 for (var j = 0; j < results.length; j++) { 3761 $('#' + id).find('.distance').html(results[j].distance.text); 3762 } 3763 } 3764 } 3765 }; 3766 } 3767 3768 // Execute Search Forms 3769 function executeSearchForm() { 3770 3771 // Get form elements as array 3772 var formData = {}; 3773 $.each($('#search-form form').serializeArray(), function (i, field) { 3774 formData[field.name] = field.value; 3775 }); 3776 3777 var keywords = formData.keywords; 3778 var types = formData.type; 3779 var rankBy = formData.rankby; 3780 3781 searchMap(keywords, types, rankBy); 3782 3783 } 3784 3785 // Events 3786 3787 // Map Loaded 3788 $(document).on('mapLoaded', function (event) { 3789 if (mapType === 'explore') { 3790 3791 openPanel(cloneGoogleLogo()); 3792 3793 } else if (mapType === 'find') { 3794 3795 if (jsData.isMobile === 'true') { 3796 mapPanel.addClass('open'); 3797 } else { 3798 openPanel(cloneGoogleLogo()); 3799 } 3800 3801 } 3802 mapContainer.addClass('loaded'); 3803 mapLoaded(); 3804 }); 3805 3806 // Close place details 3807 $('.close-detail').on('click', function (e) { 3808 closeDetails(); 3809 }); 3810 3811 // Close map 3812 $('.close-map').on('click', function (e) { 3813 closeMap(); 3814 }); 3815 3816 // Keyboard 3817 $(document).keyup(function (e) { 3818 3819 // Escape button press 3820 if (e.which === 27 && mapType === 'explore') { 3821 if (typeof mapPanel !== 'undefined' && mapPanel.hasClass('show-detail
3821s')) { 3822 closeDetails(); 3823 } else if (typeof mapContainer !== 'undefined' && mapContainer.hasClass('open')) { 3824 closeMap(); 3825 } 3826 } 3827 3828 }); 3829 3830 $(document).keydown(function (e) { 3831 3832 // Enter button press 3833 if (e.which === 13 && mapType === 'explore') { 3834 if (typeof mapPanel !== 'undefined') { 3835 var isForm = $(document.activeElement).closest('.form').length ? true : false; 3836 if (isForm) { 3837 e.preventDefault(); 3838 executeSearchForm(); 3839 } 3840 } 3841 } 3842 3843 }); 3844 3845 return { 3846 initExplore: initExplore, 3847 initFind: initFind, 3848 reinit: reinit, 3849 loaded: isMapLoaded, 3850 openMap: openMap, 3851 closeMap: closeMap, 3852 searchMap: searchMap, 3853 executeSearchForm: executeSearchForm 3854 }; 3855 3856 })(); 3857 3858 $('#search-form .submit-button').on('click', function (e) { 3859 3860 // Stop form from submitting 3861 e.preventDefault(); 3862 3863 // Track Event 3864 trackEvent('Interaction', 'explore-search', 'Searched Nearby'); 3865 3866 googleMap.executeSearchForm(); 3867 3868 }); 3869 3870 $('.show-map').on('click', function (e) { 3871 e.preventDefault(); 3872 3873 // Track Event 3874 trackEvent('Interaction', 'explore-open', 'Explored Nearby Places'); 3875 3876 if (googleMap.loaded()) { 3877 googleMap.openMap(googleMap.reinit()); 3878 } else { 3879 googleMap.initExplore(cfsData); 3880 } 3881 }); 3882 3883 if ($('#interior-find').length) { 3884 googleMap.initFind(); 3885 } 3886 3887// -------------------------------------------------------------------------- 3888// -------------------------------------------------------- 404 Page 3889// -------------------------------------------------------------------------- 3890 3891 3892// -------------------------------------------------------------------------- 3893// -------------------------------------------------------- Helper Functions 3894// -------------------------------------------------------------------------- 3895 3896 // Remove Emoji's from string 3897 function removeEmojis(string) { 3898 3899 var regex = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g; 3900 3901 return string.replace(regex, ''); 3902 } 3903 3904 function cleanTitle(string) { 3905 var str = string; 3906 str = $.trim(str).replace(/\W+/g, '-').toLowerCase(); 3907 return str; 3908 } 3909 3910 function fixEmail(email) { 3911 email = email.split('[at]').join('@'); 3912 email = email.split('[dot]').join('.'); 3913 3914 return email; 3915 } 3916 3917// Throttle calls to "callback" routine and ensure that it 3918// is not invoked any more often than "delay" milliseconds. 3919 function throttle(delay, callback) { 3920 var previousCall = new Date().getTime(); 3921 return function () { 3922 var time = new Date().getTime(); 3923 3924 if ((time - previousCall) >= delay) { 3925 previousCall = time; 3926 // Optional Callback function 3927 if (typeof callback === 'function' && callback()) { 3928 callback(); 3929 } 3930 } 3931 }; 3932 } 3933 3934 function genMailTo(email, subject) { 3935 3936 $('.format-email').each(function () { 3937 3938 var span = $('<span>').text(formatEmail(email)); 3939 3940 var mailto = $('<a>').attr({ 3941 rel: 'nofollow', 3942 class: 'track-email', 3943 href: 'mailto:' + email + '?subject=' + subject 3944 }).html(span); 3945 3946 $(this).append(mailto); 3947 3948 }); 3949 3950 } 3951 3952 function formatEmail(email) { 3953 3954 var formatted = email; 3955 3956 formatted = specialNames(formatted); 3957 3958 formatted = ucfirst(formatted); 3959 3960 formatted = formatted.replace('staycobblestone', 'StayCobblestone'); 3961 formatted = formatted.replace('stayboarders', 'StayBoarders'); 3962 3963 return formatted; 3964 } 3965 3966 function specialNames(string) { 3967 3968 // Some properties are a combination of two names 3969 // like Chippewa Falls, so this function looks for 3970 // them and sets up camel case 3971 3972 var newString = string; 3973 3974 newString = newString.replace('mccook', 'McCook'); 3975
3976 newString = newString.replace('stmarys', 'StMarys'); 3977 3978 newString = newString.replace('fortdodge', 'FortDodge'); 3979 3980 newString = newString.replace('traversecity', 'TraverseCity'); 3981 3982 newString = newString.replace('biglake', 'BigLake'); 3983 3984 newString = newString.replace('brokenbow', 'BrokenBow'); 3985 3986 newString = newString.replace('grandisland', 'GrandIsland'); 3987 3988 newString = newString.replace('devilslake', 'DevilsLake'); 3989 3990 newString = newString.replace('ashlandcity', 'AshlandCity'); 3991 3992 newString = newString.replace('chippewafalls', 'ChippewaFalls'); 3993 3994 newString = newString.replace('stevenspoint', 'StevensPoint'); 3995 3996 newString = newString.replace('sodasprings', 'SodaSprings'); 3997 3998 newString = newString.replace('fairfieldbay', 'FairfieldBay'); 3999 4000 newString = newString.replace('wirapids', 'WiRapids'); 4001 4002 newString = newString.replace('oshkoshwi', 'OshkoshWi'); 4003 4004 return newString; 4005 4006 } 4007 4008 function ucfirst(string) { 4009 return string.charAt(0).toUpperCase() + string.slice(1); 4010 } 4011 4012 if ($('.format-email').length) { 4013 if (typeof cfsData !== 'undefined' && cfsData !== null) { 4014 var email = fixEmail(cfsData.property.email); 4015 genMailTo(email, 'Message for' + ' ' + cfsData.property.type + ' in ' + cfsData.property.city); 4016 } 4017 } 4018 4019 function isInt(value) { 4020 if (isNaN(value)) { 4021 return false; 4022 } 4023 var x = parseFloat(value); 4024 return (x | 0) === x; 4025 } 4026 4027 // Which transition event 4028 // Function from David Walsh 4029 function whichTransitionEvent() { 4030 4031 //var t, el = document.createElement("fakeelement"); 4032// 4033// var transitions = { 4034// 'WebkitTransition' :'webkitTransitionEnd', 4035// 'MozTransition' :'transitionend', 4036// 'MSTransition' :'msTransitionEnd', 4037// 'OTransition' :'oTransitionEnd', 4038// 'transition' :'transitionEnd' 4039// }; 4040// 4041// for (t in transitions){ 4042// if (el.style[t] !== undefined){ 4043// return transitions[t]; 4044// } 4045// } 4046 4047 return 'webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend'; 4048 4049 } 4050 4051 function hasCustomClass(e) { 4052 4053 var customClass; 4054 4055 if ($(e).closest('.open-lightbox').length) { 4056 customClass = true; 4057 } else if ($(e).closest('.show-gallery').length) { 4058 customClass = true; 4059 } else if ($(e).closest('.show-group-booking').length) { 4060 customClass = true; 4061 } else if ($(e).closest('.show-map').length) { 4062 customClass = true; 4063 } else { 4064 customClass = false; 4065 } 4066 4067 return customClass; 4068 } 4069 4070 //Scroll smoothly 4071 $('.menu-container a[href*="#"]:not([href="#"])').click(function (e) { 4072 4073 var _offset = 0; 4074 4075 if (!hasCustomClass(this)) { 4076 if ($('#interior-booking').length) { 4077 _offset = $('#interior-booking').outerHeight() + $('#property-navigation').outerHeight(); 4078 } 4079 if (navigator.userAgent.match(/(iPod|iPhone|iPad|Android)/)) { 4080 window.scrollTo(0, $(this.hash).offset().top - _offset); 4081 } else { 4082 4083 e.preventDefault(); 4084 4085 $('html,body').stop().animate({scrollTop: $(this.hash).offset().top - _offset}, 1500); 4086 4087 if (location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') && location.hostname === this.hostname) { 4088 var target = $(this.hash); 4089 target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 4090 if (target.length) { 4091 $('html,body').stop().animate({ 4092 scrollTop: target.offset().top - _offset 4093 }, 1000); 4094 return false; 4095 } 4096 } 4097 } 4098 } 4099 }); 4100 4101 // Scroll to top 4102 $(window).scroll(function () { 4103 if ($(this).scrollTop() > 600) { 4104 $('#scroll-to-top').stop().fadeIn(); 4105 } else { 4106 $('#scroll-to-top').stop().fadeOut(); 4107 } 4108 }); 4109 4110 // scroll body to 0px on click 4111 $('#scroll-to-top').click(function () { 4112 $('body,html').stop().animate({ 4113 scrollTop: 0 4114 }, 800); 4115 return false;
4116 }); 4117 4118 // Stop scrolling on user scroll 4119 $('body, html').on('scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove', function (e) { 4120 if (e.which > 0 || e.type === "mousedown" || e.type === "mousewheel" || e.type === "touchmove") { 4121 $('html, body').stop(true); 4122 } 4123 }); 4124 4125 // Form Validation 4126 var validate = (function () { 4127 4128 function checkFormData(form) { 4129 4130 // Determine what type of element passed 4131 var $form; 4132 if (typeof form !== 'undefined') { 4133 4134 if (typeof form === 'string') { 4135 $form = $(form); 4136 } else { 4137 $form = form; 4138 } 4139 4140 // Get form elements as array 4141 var formData = {}; 4142 $.each($form.serializeArray(), function (i, field) { 4143 var $field = $form.find('[name=' + field.name + ']'); 4144 if ($field.hasAnyClass('required', 'required-email', 'required-phone', 'required-num', 'required-nonum')) { 4145 var data = { 4146 value: field.value, 4147 class: $field.attr('class'), 4148 target: $field 4149 }; 4150 formData[field.name] = data; 4151 } 4152 }); 4153 4154 // Send to validation and get results 4155 var results = validateForm(formData); 4156 4157 4158 // Check validation results 4159 if ($.isEmptyObject(results)) { 4160 // If it's an empty object the validation passed 4161 return true; 4162 } else { 4163 // Otherwise it will contain the failed elements 4164 // Handle indicating troublesome elements 4165 $.each(results, function (i, e) { 4166 $('label[for=' + e.attr('name') + ']').addClass('has-error'); 4167 }); 4168 return false; 4169 } 4170 4171 } else { 4172 // Form is undefined 4173 return false; 4174 } 4175 4176 } 4177 4178 function validateForm(data) { 4179 4180 var failedValidation = {}; 4181 4182 $.each(data, function (n, field) { 4183 4184 var v = field.value; 4185 v = $.trim(field.value); 4186 var fail = false; 4187 4188 if (field.class.indexOf('required') > -1) { 4189 if (v.length === 0 || Number(v) === -1) { 4190 fail = true; 4191 } 4192 } 4193 4194 if (field.class.indexOf('required-email') > -1) { 4195 4196 if (v.length === 0) { 4197 fail = true; 4198 } else { 4199 var emailPattern = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/; 4200 if (!emailPattern.test(v)) { 4201 fail = true; 4202 } 4203 } 4204 4205 } 4206 4207 if (field.class.indexOf('required-phone') > -1) { 4208 if (v.length === 0) { 4209 fail = true; 4210 } else { 4211 var phonePattern = /^(\+\d{1,2}\s?)?(\(?\d{3}\)?([\s.-])?)?\d{3}([\s.-])?\d{4}$/; 4212 if (!phonePattern.test(v)) { 4213 fail = true; 4214 } 4215 } 4216 } 4217 4218 if (field.class.indexOf('required-num') > -1) { 4219 if (v.length === 0) { 4220 fail = true; 4221 } else { 4222 var numPattern = /^[0-9]*$/; 4223 if (!numPattern.test(v)) { 4224 fail = true; 4225 } 4226 } 4227 } 4228 4229 if (field.class.indexOf('required-nonum') > -1) { 4230 if (v.length === 0) { 4231 fail = true; 4232 } else { 4233 var noNumPattern = /^[a-zA-z\,\.\-\'\`\s]*$/; 4234 if (!noNumPattern.test(v)) { 4235 fail = true; 4236 } 4237 } 4238 } 4239 4240 if (fail) { 4241 failedValidation[n] = field.target; 4242 } 4243 4244 }); 4245 4246 return failedValidation; 4247 4248 } 4249 4250 function setRequired(form) { 4251 4252 var $form; 4253 if (typeof form !== 'undefined') { 4254 if (typeof form === 'string') { 4255 $form = $(form); 4256 } else { 4257 $form = form; 4258 } 4259 } 4260 4261 var required = $form.find(':input[class*="required"]'); 4262 4263 $.each(required, function (i, v) { 4264 $(v).siblings('label').addClass('required'); 4265 }); 4266 4267 } 4268 4269 function clearError(e) { 4270 var $e = $('label[for=' + e.name + ']'); 4271 if ($e.hasClass('has-error')) { 4272 $e.removeClass('has-error'); 4273 } 4274 } 4275 4276 // return 4277 return { 4278 form: checkFormData, 4279 setRequired: setRequired, 4280 clearError: clearError 4281 }; 4282 4283 })(); // End validateForm 4284 4285 // Check for any number of classes 4286 $.fn.hasAnyClass = function () { 4287 for (var i = 0; i < arguments.length; i++) { 4288 if (this.hasClass(arguments[i])) { 4289 return true; 4290 } 4291 } 4292 return false;
4293 }; 4294 4295 // Calendars 4296 function hideCalendars() { 4297 if ($('.Zebra_DatePicker').length) { 4298 if ($('.Zebra_DatePicker').hasClass('dp_visible')) { 4299 $('.Zebra_DatePicker').removeClass('dp_visible').addClass('dp_hidden'); 4300 } 4301 } 4302 } 4303 4304 $(window).scroll(function () { 4305 hideCalendars(); 4306 }); 4307 4308 // Prevent Flicker of Unstyled Content (FOUC) 4309 $(window).on('load', function () { 4310 noFouc(); 4311 }); 4312 4313 function noFouc() { 4314 $('.no-fouc').removeClass('no-fouc'); 4315 } 4316 4317// -------------------------------------------------------------------------- 4318// --------------------------------------- Guest Experience Survey Functions 4319// -------------------------------------------------------------------------- 4320 4321 // Cobblestone Guest Experience Survey Functions 4322 // by Jeramiah Harland 4323 // Updated to TripAdvisor 2/1/17 4324 4325 // Ajax to get comments 4326 if ($('#ges').length) { 4327 4328 // Global variables 4329 var gesArray; 4330 var gesCurrent = -1; 4331 var gesInterval = setInterval(gesTimer, 10000); 4332 4333 } 4334 4335 function setGesArray(data) { 4336 4337 var commentArray = []; 4338 $.each(data.reviews, function () { 4339 if (this.rating > 3) { 4340 var comment = []; 4341 comment.title = $.trim(removeEmojis(this.title)); 4342 comment.rating = this.rating; 4343 comment.rating_image_url = this.rating_image_url; 4344 comment.username = this.user.username; 4345 comment.location = this.user.user_location.name; 4346 comment.text = this.text; 4347 comment.url = this.url; 4348 commentArray.push(comment); 4349 } 4350 }); 4351 4352 // Set the global variable 4353 gesArray = commentArray; 4354 // Initial cycle 4355 cycleGes(); 4356 } 4357 4358 function cycleGes() { 4359 4360 if (typeof gesArray !== "undefined" && gesArray !== null && gesArray.length > 0 && $.isArray(gesArray)) { 4361 4362 // Random number between 0 and the length of the array 4363 var i = randomInt(0, gesArray.length - 1); 4364 4365 while (gesCurrent !== -1 && i === gesCurrent && gesArray.length !== 1) { 4366 i = randomInt(0, gesArray.length - 1); 4367 } 4368 4369 // Set the current index so that we know not to show it twice in a row 4370 gesCurrent = i; 4371 4372 // Set content variables 4373 // gesArray is a global variable 4374 var content = gesArray[i]; 4375 4376 // If the content is visible, fade it out, set it, fade it in 4377 if ($('#ges .content').is(':visible')) { 4378 $('#ges .content').fadeOut(function () { 4379 setContent(content); 4380 }); 4381 } else { 4382 // If the content is not visible, set it and fade GES in 4383 // Add class loaded so that CSS3 transitions work after initial load 4384 setContent(content, function () { 4385 $('#ges').fadeIn(function () { 4386 $(this).addClass('loaded'); 4387 }); 4388 }); 4389 } 4390 4391 } else { 4392 $('#ges').hide(); 4393 } 4394 4395 } 4396 4397 function closeGes() { 4398 $('#ges').removeClass('loaded').fadeOut(function () { 4399 clearInterval(gesInterval); 4400 }); 4401 } 4402 4403 function setContent(content, callback) { 4404 4405 $('#ges .content').fadeOut(function () { 4406 // Set html of content elements 4407 $('#ges .content .title').html('"' + content.title + '"'); 4408 if (content.title.length > 28) { 4409 $('#ges .content .title').addClass('long-title'); 4410 } else { 4411 $('#ges .content .title').removeClass('long-title'); 4412 } 4413 $('#ges .content .rating-image').attr({ 4414 src: content.rating_image_url, 4415 alt: 'TripAdivsor Rating of ' + content.rating 4416 4417 }); 4418 4419 var usernameLocation = content.username; 4420 if (content.location !== null) { 4421 usernameLocation += ' - ' + content.location; 4422 } 4423 4424 $('#ges .content .username-location').html(usernameLocation); 4425 4426 var readmore = '<a class="read-more track-read-more" target="_blank" href="' + content.url + '">[Read More]</a>'; 4427 4428 $('#ges .content .text').html(content.text + ' ').append(readmore); 4429 4430 $('#ges .content').fadeIn(function () { 4431 // Optional Callback function 4432 if (typeof callback === 'function' && callback()) { 4433 callback(); 4434 } 4435 }); 4436 }); 4437 } 4438 4439 function randomInt(minimum, maximum) { 4440 var _min = Number(minimum); 4441 var _max = Number(maximum); 4442 return Math.floor(Math.random() * (_max - _min + 1) + _min); 4443 } 4444 4445 function gesTimer() { 4446 cycleGes(); 4447 } 4448 4449 $(document).on('mouseenter', '#ges', function () { 4450 clearInterval(gesInterval); 4451 }); 4452 4453 $(document).on('mouseleave', '#ges', function () { 4454 if ($('#ges').is(':visible') && !$('#page-overlay').is(':visible')) { 4455 gesInterval = setInterval(gesTimer, 10000); 4456 } 4457 }); 4458 4459 $(document).on('click', '#ges .content', function (e) { 4460 if (!$(e.target).hasClass('read-more')) { 4461 trackEvent('Interaction', 'click-cycle-review', 'Cycled TripAdvisor Review on Slider'); 4462 cycleGes(); 4463 } 4464 }); 4465 4466 $(document).on('click', '#ges .icon-close', function () { 4467 closeGes(); 4468 }); 4469 4470 $(document).on('click', '#full-comment-lightbox .icon-close', function () { 4471 gesInterval = setInterval(gesTimer, 10000); 4472 }); 4473 4474 4475// -------------------------------------------------------------------------- 4476// ------------------------------------------------------ Analytics Tracking 4477// -------------------------------------------------------------------------- 4478 4479 function virtualPageView(page) { 4480 4481 // ga is used by Google Analytics 4482
4483 page = cleanTitle(page); 4484 4485 try { 4486 4487 if (typeof (ga) === 'undefined') { 4488 throw 'analytics undefined'; 4489 } else { 4490 // Send virtual pageview 4491 ga('send', { 4492 hitType: 'pageView', 4493 page: 'virtual/' + page 4494 }); 4495 } 4496 4497 } catch (err) { 4498 4499 console.log(err); 4500 4501 } 4502 4503 } 4504 4505 function trackEvent(category, action, label, value, interaction) { 4506 4507 // ga is used by Google Analytics 4508 4509 // Optional 4510 value = value; 4511 if (typeof value === 'undefined' || !isInt(value)) { 4512 value = 0; 4513 } 4514 4515 // Optional 4516 interaction = interaction; 4517 if (typeof interaction === 'undefined') { 4518 interaction = true; 4519 } 4520 4521 try { 4522 4523 if (typeof (ga) === 'undefined') { 4524 throw 'analytics undefined'; 4525 } else { 4526 // Track event 4527 ga('send', { 4528 hitType: 'event', 4529 eventCategory: category, 4530 eventAction: action, 4531 eventLabel: label, 4532 eventValue: value, 4533 nonInteraction: interaction 4534 }); 4535 } 4536 4537 } catch (err) { 4538 4539 console.log(err); 4540 4541 } 4542 4543 } 4544 4545 // Track event for phone calls from mobile 4546 $('body').on('click', '.track-call', function (e) { 4547 trackEvent('Reservations', 'call-site', 'Call From Site', 5); 4548 }); 4549 4550 $('body').on('click', '.track-book', function (e) { 4551 trackEvent('Reservations', 'click-book', 'Clicked Direct Link To Booking', 5); 4552 }); 4553 4554 // Track event for modify reservation desktop 4555 $('body').on('click', '.track-modify', function (e) { 4556 trackEvent('Reservations', 'click-modify', 'Clicked Modify Reservation', 1); 4557 }); 4558 4559 // Track event for email clicks 4560 $('body').on('click', '.track-email', function (e) { 4561 trackEvent('Information Request', 'email-link', 'Clicked Email Link', 1); 4562 }); 4563 4564 // Track event for footer menu 4565 $('body').on('click', '#footer-menu-navigation a', function (e) { 4566 trackEvent('Navigation', 'click-footer', 'Clicked Link In Footer'); 4567 }); 4568 4569 // Track event for property main menu menu-mobile-property-navigation 4570 $('body').on('click', '#menu-main-property-navigation a', function (e) { 4571 trackEvent('Navigation', 'click-property-menu', 'Clicked Link In Property Menu'); 4572 }); 4573 4574 // Track event for property mobile menu 4575 $('body').on('click', '#menu-mobile-property-navigation a', function (e) { 4576 trackEvent('Navigation', 'click-property-mobile-menu', 'Clicked Link In Property Mobile Menu'); 4577 }); 4578 4579 // Track event for default menu (home mobile, 404, map) 4580 $('body').on('click', '#menu-default-navigation a', function (e) { 4581 trackEvent('Navigation', 'click-default-menu', 'Clicked Link In Default Menu'); 4582 }); 4583 4584 // Track event for social media 4585 $('body').on('click', '.track-facebook', function (e) { 4586 trackEvent('Social Media', 'click-fb', 'Clicked Facebook'); 4587 }); 4588 4589 $('body').on('click', '.track-twitter', function (e) { 4590 trackEvent('Social Media', 'click-twitter', 'Clicked Twitter'); 4591 }); 4592 4593 $('body').on('click', '.track-google_plus', function (e) { 4594 trackEvent('Social Media', 'click-gplus', 'Clicked Google Plus'); 4595 }); 4596 4597 $('body').on('click', '.track-linkedin', function (e) { 4598 trackEvent('Social Media', 'click-linkedin', 'Clicked LinkedIn'); 4599 }); 4600 4601 $('body').on('click', '.track-pinterest', function (e) { 4602 trackEvent('Social Media', 'click-pinterest', 'Clicked Pinterest'); 4603 }); 4604 4605 $('body').on('click', '.track-youtube', function (e) { 4606 trackEvent('Social Media', 'click-youtube', 'Clicked YouTube'); 4607 }); 4608 4609 // Track event for custom link inside of homepage or property slider 4610 $('body').on('click', '.track-custom-link', function (e) { 4611 trackEvent('Interaction', 'click-custom', 'Clicked Link In Slider'); 4612 }); 4613 4614 // Track event for what's new section 4615 $('body').on('click', '.track-whats-new', function (e) {
4616 var targetTitle = cleanTitle($(this).closest('.whats-new-image').find('h3').text()); 4617 if (typeof (targetTitle) === 'undefined' || targetTitle === '') { 4618 targetTitle = 'whats-new'; 4619 } 4620 trackEvent('Navigation', 'click-' + targetTitle, 'Clicked Link In Whats New', 1); 4621 }); 4622 4623 // Track event for coming soon 4624 $('body').on('click', '.track-coming-soon', function (e) { 4625 var targetTitle = cleanTitle($(this).find('.location').text()); 4626 if (typeof (targetTitle) === 'undefined' || targetTitle === '') { 4627 targetTitle = 'coming-soon'; 4628 } 4629 trackEvent('Navigation', 'click-' + targetTitle, 'Clicked Link In Coming Soon', 1); 4630 }); 4631 4632 // Track event for homepage 'Find a cobblestone' button 4633 $('body').on('click', '.track-find', function (e) { 4634 trackEvent('Navigation', 'click-find', 'Clicked Find A Cobblestone'); 4635 }); 4636 4637 // Track event for homepage 'Franchise Opportunities' button 4638 $('body').on('click', '.track-franchise', function (e) { 4639 trackEvent('Navigation', 'click-franchise', 'Clicked Franchise Opportunities'); 4640 }); 4641 4642 // Track event for navigation from map 4643 $('body').on('click', '.track-visit-page', function (e) { 4644 trackEvent('Navigation', 'click-visit-page', 'Clicked Visit Page'); 4645 }); 4646 4647 $('body').on('click', '.track-logo-link', function (e) { 4648 trackEvent('Navigation', 'click-logo-link', 'Clicked Logo Link'); 4649 }); 4650 4651 $('body').on('click', '.track-map-it', function (e) { 4652 trackEvent('Navigation', 'click-map-it', 'Clicked Map It Link'); 4653 }); 4654 4655 $('body').on('click', '.track-read-review', function (e) { 4656 trackEvent('Navigation', 'click-read-review', 'Clicked TripAdvisor Read Review Link'); 4657 }); 4658 4659 $('body').on('click', '.track-write-review', function (e) { 4660 trackEvent('Navigation', 'click-write-review', 'Clicked TripAdvisor Write Review Link'); 4661 }); 4662 4663 // TripAdvisor comment events 4664 $('body').on('click', '.track-read-more', function (e) { 4665 trackEvent('Navigation', 'click-ta-read-more', 'Clicked Read More in TripAdvisor Review'); 4666 }); 4667 4668// -------------------------------------------------------------------------- 4669// -------------------------------------------------------------- Sliders 4670// -------------------------------------------------------------------------- 4671 4672 // Home Page Slider 4673 // On init and afterChange, fade in any hidden custom slides 4674 $("#homepage-slider .slider").on("init", function (event, slick) { 4675 scrollingFeatures.updateHeight(); 4676 $("#homepage-slider .slider .slick-active").find('.custom-slide').fadeIn(); 4677 }); 4678 $("#homepage-slider .slider").on("afterChange", function (event, slick, currentSlide) { 4679 $("#homepage-slider .slider .slick-active").find('.custom-slide').fadeIn(); 4680 }); 4681 4682 $(window).on('load', function () { 4683 4684 // Initialize Home Page Slider 4685 $("#homepage-slider .slider").slick({ 4686 autoplay: true, 4687 loop: true, 4688 slidesToShow: 1, 4689 slidesToScroll: 1, 4690 arrows: false, 4691 dots: true, 4692 autoplaySpeed: 6000, 4693 speed: 1000, 4694 fade: true, 4695 accessibility: false, 4696 pauseOnHover: true 4697 }); 4698 4699 $("#homepage-hero .slider .slick-active").addClass("loaded"); 4700 4701 }); 4702 4703 // Property Page Slider 4704 // On init and afterChange, fade in any hidden custom slides 4705 $("#property-slider .slider").on("init", function (event, slick) { 4706 scrollingFeatures.updateHeight(); 4707 $("#property-slider .slider .slick-active").find('.custom-slide').fadeIn(); 4708 }); 4709 $("#property-slider .slider").on("afterChange", function (event, slick, currentSlide) { 4710 $("#property-slider .slider .slick-active").find('.custom-slide').fadeIn(); 4711 }); 4712 4713 $(window).on('load', function () { 4714 4715 // Initialize Property Page Slider 4716 $("#property-slider .slider").slick({ 4717 autoplay: true, 4718 loop: true, 4719 slidesToShow: 1, 4720 slidesToScroll: 1,
4721 arrows: false, 4722 dots: true, 4723 autoplaySpeed: 6000, 4724 speed: 1000, 4725 fade: true, 4726 accessibility: false, 4727 pauseOnHover: true, 4728 useTransform: true 4729 }); 4730 4731 $("#property-slider, #property-slider .slider .slick-active").addClass("loaded"); 4732 4733 }); 4734 4735// -------------------------------------------------------------------------- 4736// ------------------------------------------------------ Coming Soon Slider 4737// -------------------------------------------------------------------------- 4738 4739 var comingSoon = (function () { 4740 4741 function init() { 4742 if (typeof (jsData) !== "undefined" && jsData !== null) { 4743 4744 $.get(jsData.ajaxURL, 4745 { 4746 action: 'get_coming_soon', 4747 }, 4748 function (response) { 4749 createComingSoon(response); 4750 }); 4751 } 4752 } 4753 4754 function createComingSoon(response) { 4755 4756 $.each(response, function (i, property) { 4757 4758 var a = $('<a>').attr({ 4759 href: property.link 4760 }); 4761 4762 var img = $('<img>').attr({ 4763 src: property.alt_logo, 4764 alt: property.type 4765 }).appendTo(a); 4766 4767 var location = $('<span>').attr({ 4768 class: 'location cobble-font', 4769 }).text(property.city + ', ' + property.state).appendTo(a); 4770 4771 var description = $('<div>').attr({ 4772 class: 'description', 4773 }).text(property.status_description).appendTo(a); 4774 4775 $('<div>').html(a).appendTo('#coming-soon .slider'); 4776 4777 $('<div class="coming-soon-property">').html(a.clone()).appendTo('#coming-soon .mobile-properties-list'); 4778 4779 }); 4780 4781 initSlider(); 4782 4783 } 4784 4785 function initSlider() { 4786 $('#coming-soon .slider').slick({ 4787 autoplay: true, 4788 loop: true, 4789 slidesToShow: 4, 4790 slidesToScroll: 1, 4791 arrows: true, 4792 dots: false, 4793 autoplaySpeed: 6000, 4794 speed: 1000, 4795 pauseOnHover: true 4796 }); 4797 } 4798 4799 $('#coming-soon .slider').on('init', function (event, slick) { 4800 $('#coming-soon .loader').fadeOut(function () { 4801 $('#coming-soon .slider').css('visibility', 'visible').fadeIn(); 4802 }); 4803 }); 4804 4805 // return 4806 return { 4807 init: init 4808 }; 4809 4810 })(); // End comingSoon 4811 4812 if ($('#coming-soon').length) { 4813 comingSoon.init(); 4814 } 4815 4816// triggering a tag for modify reservation 4817 if ($('a.modify.track-modify').attr('data-ivhtravel') == 'WebRezPro' || $('a.modify.track-modify').attr('data-ivhtravel') == 'IVHTravel') { 4818 $('a.track-modify').attr('href', '#'); 4819 $('a.track-modify').on('click', function () { 4820 $('span.book-now-form').trigger('click'); 4821 return false; 4822 }); 4823 } 4824}); // End Document Ready
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.