single-product.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*global wc_single_product_params, PhotoSwipe, PhotoSwipeUI_Default */
  2. jQuery( function( $ ) {
  3. // wc_single_product_params is required to continue.
  4. if ( typeof wc_single_product_params === 'undefined' ) {
  5. return false;
  6. }
  7. $( 'body' )
  8. // Tabs
  9. .on( 'init', '.wc-tabs-wrapper, .woocommerce-tabs', function() {
  10. $( '.wc-tab, .woocommerce-tabs .panel:not(.panel .panel)' ).hide();
  11. var hash = window.location.hash;
  12. var url = window.location.href;
  13. var $tabs = $( this ).find( '.wc-tabs, ul.tabs' ).first();
  14. if ( hash.toLowerCase().indexOf( 'comment-' ) >= 0 || hash === '#reviews' || hash === '#tab-reviews' ) {
  15. $tabs.find( 'li.reviews_tab a' ).click();
  16. } else if ( url.indexOf( 'comment-page-' ) > 0 || url.indexOf( 'cpage=' ) > 0 ) {
  17. $tabs.find( 'li.reviews_tab a' ).click();
  18. } else if ( hash === '#tab-additional_information' ) {
  19. $tabs.find( 'li.additional_information_tab a' ).click();
  20. } else {
  21. $tabs.find( 'li:first a' ).click();
  22. }
  23. } )
  24. .on( 'click', '.wc-tabs li a, ul.tabs li a', function( e ) {
  25. e.preventDefault();
  26. var $tab = $( this );
  27. var $tabs_wrapper = $tab.closest( '.wc-tabs-wrapper, .woocommerce-tabs' );
  28. var $tabs = $tabs_wrapper.find( '.wc-tabs, ul.tabs' );
  29. $tabs.find( 'li' ).removeClass( 'active' );
  30. $tabs_wrapper.find( '.wc-tab, .panel:not(.panel .panel)' ).hide();
  31. $tab.closest( 'li' ).addClass( 'active' );
  32. $tabs_wrapper.find( $tab.attr( 'href' ) ).show();
  33. } )
  34. // Review link
  35. .on( 'click', 'a.woocommerce-review-link', function() {
  36. $( '.reviews_tab a' ).click();
  37. return true;
  38. } )
  39. // Star ratings for comments
  40. .on( 'init', '#rating', function() {
  41. $( '#rating' ).hide().before( '<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>' );
  42. } )
  43. .on( 'click', '#respond p.stars a', function() {
  44. var $star = $( this ),
  45. $rating = $( this ).closest( '#respond' ).find( '#rating' ),
  46. $container = $( this ).closest( '.stars' );
  47. $rating.val( $star.text() );
  48. $star.siblings( 'a' ).removeClass( 'active' );
  49. $star.addClass( 'active' );
  50. $container.addClass( 'selected' );
  51. return false;
  52. } )
  53. .on( 'click', '#respond #submit', function() {
  54. var $rating = $( this ).closest( '#respond' ).find( '#rating' ),
  55. rating = $rating.val();
  56. if ( $rating.length > 0 && ! rating && wc_single_product_params.review_rating_required === 'yes' ) {
  57. window.alert( wc_single_product_params.i18n_required_rating_text );
  58. return false;
  59. }
  60. } );
  61. // Init Tabs and Star Ratings
  62. $( '.wc-tabs-wrapper, .woocommerce-tabs, #rating' ).trigger( 'init' );
  63. /**
  64. * Product gallery class.
  65. */
  66. var ProductGallery = function( $target, args ) {
  67. this.$target = $target;
  68. this.$images = $( '.woocommerce-product-gallery__image', $target );
  69. // No images? Abort.
  70. if ( 0 === this.$images.length ) {
  71. this.$target.css( 'opacity', 1 );
  72. return;
  73. }
  74. // Make this object available.
  75. $target.data( 'product_gallery', this );
  76. // Pick functionality to initialize...
  77. this.flexslider_enabled = $.isFunction( $.fn.flexslider ) && wc_single_product_params.flexslider_enabled;
  78. this.zoom_enabled = $.isFunction( $.fn.zoom ) && wc_single_product_params.zoom_enabled;
  79. this.photoswipe_enabled = typeof PhotoSwipe !== 'undefined' && wc_single_product_params.photoswipe_enabled;
  80. // ...also taking args into account.
  81. if ( args ) {
  82. this.flexslider_enabled = false === args.flexslider_enabled ? false : this.flexslider_enabled;
  83. this.zoom_enabled = false === args.zoom_enabled ? false : this.zoom_enabled;
  84. this.photoswipe_enabled = false === args.photoswipe_enabled ? false : this.photoswipe_enabled;
  85. }
  86. // ...and what is in the gallery.
  87. if ( 1 === this.$images.length ) {
  88. this.flexslider_enabled = false;
  89. }
  90. // Bind functions to this.
  91. this.initFlexslider = this.initFlexslider.bind( this );
  92. this.initZoom = this.initZoom.bind( this );
  93. this.initZoomForTarget = this.initZoomForTarget.bind( this );
  94. this.initPhotoswipe = this.initPhotoswipe.bind( this );
  95. this.onResetSlidePosition = this.onResetSlidePosition.bind( this );
  96. this.getGalleryItems = this.getGalleryItems.bind( this );
  97. this.openPhotoswipe = this.openPhotoswipe.bind( this );
  98. if ( this.flexslider_enabled ) {
  99. this.initFlexslider();
  100. $target.on( 'woocommerce_gallery_reset_slide_position', this.onResetSlidePosition );
  101. } else {
  102. this.$target.css( 'opacity', 1 );
  103. }
  104. if ( this.zoom_enabled ) {
  105. this.initZoom();
  106. $target.on( 'woocommerce_gallery_init_zoom', this.initZoom );
  107. }
  108. if ( this.photoswipe_enabled ) {
  109. this.initPhotoswipe();
  110. }
  111. };
  112. /**
  113. * Initialize flexSlider.
  114. */
  115. ProductGallery.prototype.initFlexslider = function() {
  116. var $target = this.$target,
  117. gallery = this;
  118. var options = $.extend( {
  119. selector: '.woocommerce-product-gallery__wrapper > .woocommerce-product-gallery__image',
  120. start: function() {
  121. $target.css( 'opacity', 1 );
  122. },
  123. after: function( slider ) {
  124. gallery.initZoomForTarget( gallery.$images.eq( slider.currentSlide ) );
  125. }
  126. }, wc_single_product_params.flexslider );
  127. $target.flexslider( options );
  128. // Trigger resize after main image loads to ensure correct gallery size.
  129. $( '.woocommerce-product-gallery__wrapper .woocommerce-product-gallery__image:eq(0) .wp-post-image' ).one( 'load', function() {
  130. var $image = $( this );
  131. if ( $image ) {
  132. setTimeout( function() {
  133. var setHeight = $image.closest( '.woocommerce-product-gallery__image' ).height();
  134. var $viewport = $image.closest( '.flex-viewport' );
  135. if ( setHeight && $viewport ) {
  136. $viewport.height( setHeight );
  137. }
  138. }, 100 );
  139. }
  140. } ).each( function() {
  141. if ( this.complete ) {
  142. $( this ).trigger( 'load' );
  143. }
  144. } );
  145. };
  146. /**
  147. * Init zoom.
  148. */
  149. ProductGallery.prototype.initZoom = function() {
  150. this.initZoomForTarget( this.$images.first() );
  151. };
  152. /**
  153. * Init zoom.
  154. */
  155. ProductGallery.prototype.initZoomForTarget = function( zoomTarget ) {
  156. if ( ! this.zoom_enabled ) {
  157. return false;
  158. }
  159. var galleryWidth = this.$target.width(),
  160. zoomEnabled = false;
  161. $( zoomTarget ).each( function( index, target ) {
  162. var image = $( target ).find( 'img' );
  163. if ( image.data( 'large_image_width' ) > galleryWidth ) {
  164. zoomEnabled = true;
  165. return false;
  166. }
  167. } );
  168. // But only zoom if the img is larger than its container.
  169. if ( zoomEnabled ) {
  170. var zoom_options = $.extend( {
  171. touch: false
  172. }, wc_single_product_params.zoom_options );
  173. if ( 'ontouchstart' in document.documentElement ) {
  174. zoom_options.on = 'click';
  175. }
  176. zoomTarget.trigger( 'zoom.destroy' );
  177. zoomTarget.zoom( zoom_options );
  178. }
  179. };
  180. /**
  181. * Init PhotoSwipe.
  182. */
  183. ProductGallery.prototype.initPhotoswipe = function() {
  184. if ( this.zoom_enabled && this.$images.length > 0 ) {
  185. this.$target.prepend( '<a href="#" class="woocommerce-product-gallery__trigger">🔍</a>' );
  186. this.$target.on( 'click', '.woocommerce-product-gallery__trigger', this.openPhotoswipe );
  187. this.$target.on( 'click', '.woocommerce-product-gallery__image a', function( e ) {
  188. e.preventDefault();
  189. });
  190. // If flexslider is disabled, gallery images also need to trigger photoswipe on click.
  191. if ( ! this.flexslider_enabled ) {
  192. this.$target.on( 'click', '.woocommerce-product-gallery__image a', this.openPhotoswipe );
  193. }
  194. } else {
  195. this.$target.on( 'click', '.woocommerce-product-gallery__image a', this.openPhotoswipe );
  196. }
  197. };
  198. /**
  199. * Reset slide position to 0.
  200. */
  201. ProductGallery.prototype.onResetSlidePosition = function() {
  202. this.$target.flexslider( 0 );
  203. };
  204. /**
  205. * Get product gallery image items.
  206. */
  207. ProductGallery.prototype.getGalleryItems = function() {
  208. var $slides = this.$images,
  209. items = [];
  210. if ( $slides.length > 0 ) {
  211. $slides.each( function( i, el ) {
  212. var img = $( el ).find( 'img' );
  213. if ( img.length ) {
  214. var large_image_src = img.attr( 'data-large_image' ),
  215. large_image_w = img.attr( 'data-large_image_width' ),
  216. large_image_h = img.attr( 'data-large_image_height' ),
  217. item = {
  218. src : large_image_src,
  219. w : large_image_w,
  220. h : large_image_h,
  221. title: img.attr( 'data-caption' ) ? img.attr( 'data-caption' ) : img.attr( 'title' )
  222. };
  223. items.push( item );
  224. }
  225. } );
  226. }
  227. return items;
  228. };
  229. /**
  230. * Open photoswipe modal.
  231. */
  232. ProductGallery.prototype.openPhotoswipe = function( e ) {
  233. e.preventDefault();
  234. var pswpElement = $( '.pswp' )[0],
  235. items = this.getGalleryItems(),
  236. eventTarget = $( e.target ),
  237. clicked;
  238. if ( eventTarget.is( '.woocommerce-product-gallery__trigger' ) || eventTarget.is( '.woocommerce-product-gallery__trigger img' ) ) {
  239. clicked = this.$target.find( '.flex-active-slide' );
  240. } else {
  241. clicked = eventTarget.closest( '.woocommerce-product-gallery__image' );
  242. }
  243. var options = $.extend( {
  244. index: $( clicked ).index()
  245. }, wc_single_product_params.photoswipe_options );
  246. // Initializes and opens PhotoSwipe.
  247. var photoswipe = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options );
  248. photoswipe.init();
  249. };
  250. /**
  251. * Function to call wc_product_gallery on jquery selector.
  252. */
  253. $.fn.wc_product_gallery = function( args ) {
  254. new ProductGallery( this, args );
  255. return this;
  256. };
  257. /*
  258. * Initialize all galleries on page.
  259. */
  260. $( '.woocommerce-product-gallery' ).each( function() {
  261. $( this ).wc_product_gallery();
  262. } );
  263. } );