shoplazza-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.editorconfig +28 -0
  2. package/.prettierrc +9 -0
  3. package/LICENSE +21 -0
  4. package/README.md +208 -0
  5. package/bin/shoplazza +117 -0
  6. package/fixtures/assets/blog.scss +74 -0
  7. package/fixtures/assets/cart_modal.scss +450 -0
  8. package/fixtures/assets/collection_detail.js +234 -0
  9. package/fixtures/assets/collection_detail.scss +345 -0
  10. package/fixtures/assets/collection_list.scss +11 -0
  11. package/fixtures/assets/collection_slider.scss +169 -0
  12. package/fixtures/assets/feature_columns.scss +26 -0
  13. package/fixtures/assets/feature_product.scss +109 -0
  14. package/fixtures/assets/footer.js +58 -0
  15. package/fixtures/assets/footer.scss +337 -0
  16. package/fixtures/assets/four_images.scss +29 -0
  17. package/fixtures/assets/gallery.scss +55 -0
  18. package/fixtures/assets/header.js +178 -0
  19. package/fixtures/assets/header.scss +929 -0
  20. package/fixtures/assets/image_text.scss +72 -0
  21. package/fixtures/assets/logo_bar.scss +11 -0
  22. package/fixtures/assets/newsletter.scss +90 -0
  23. package/fixtures/assets/not_found.scss +39 -0
  24. package/fixtures/assets/page_detail.scss +16 -0
  25. package/fixtures/assets/pagination.scss +150 -0
  26. package/fixtures/assets/postcss.config.js +6 -0
  27. package/fixtures/assets/product_description.scss +88 -0
  28. package/fixtures/assets/product_detail.js +634 -0
  29. package/fixtures/assets/product_detail.scss +1106 -0
  30. package/fixtures/assets/relative_product.scss +45 -0
  31. package/fixtures/assets/reviews.scss +70 -0
  32. package/fixtures/assets/rich_text.scss +71 -0
  33. package/fixtures/assets/search.js +87 -0
  34. package/fixtures/assets/search.scss +67 -0
  35. package/fixtures/assets/slide.scss +51 -0
  36. package/fixtures/assets/slider.scss +141 -0
  37. package/fixtures/assets/theme.css +976 -0
  38. package/fixtures/assets/theme.scss +1100 -0
  39. package/fixtures/assets/three_images.scss +20 -0
  40. package/fixtures/assets/tools.scss +23 -0
  41. package/fixtures/assets/two_images.scss +24 -0
  42. package/fixtures/assets/video.scss +45 -0
  43. package/fixtures/assets/video_text.scss +63 -0
  44. package/fixtures/config/settings_data.json +107 -0
  45. package/fixtures/config/settings_schema.json +690 -0
  46. package/fixtures/layout/theme.liquid +76 -0
  47. package/fixtures/locales/ar-SA.json +212 -0
  48. package/fixtures/locales/de-DE.json +290 -0
  49. package/fixtures/locales/en-US.json +290 -0
  50. package/fixtures/locales/es-ES.json +290 -0
  51. package/fixtures/locales/fr-FR.json +290 -0
  52. package/fixtures/locales/id-ID.json +212 -0
  53. package/fixtures/locales/it-IT.json +212 -0
  54. package/fixtures/locales/ja-JP.json +289 -0
  55. package/fixtures/locales/ko-KR.json +290 -0
  56. package/fixtures/locales/nl-NL.json +290 -0
  57. package/fixtures/locales/pl-PL.json +290 -0
  58. package/fixtures/locales/pt-PT.json +212 -0
  59. package/fixtures/locales/ru-RU.json +212 -0
  60. package/fixtures/locales/th-TH.json +212 -0
  61. package/fixtures/locales/zh-CN.json +290 -0
  62. package/fixtures/locales/zh-TW.json +290 -0
  63. package/fixtures/sections/apps.liquid +47 -0
  64. package/fixtures/sections/blog.liquid +137 -0
  65. package/fixtures/sections/collection_desc.liquid +34 -0
  66. package/fixtures/sections/collection_detail.liquid +436 -0
  67. package/fixtures/sections/collection_image.liquid +104 -0
  68. package/fixtures/sections/collection_list.liquid +161 -0
  69. package/fixtures/sections/collection_name.liquid +34 -0
  70. package/fixtures/sections/collection_slider.liquid +330 -0
  71. package/fixtures/sections/feature_columns.liquid +275 -0
  72. package/fixtures/sections/feature_product.liquid +227 -0
  73. package/fixtures/sections/footer.liquid +488 -0
  74. package/fixtures/sections/four_images.liquid +160 -0
  75. package/fixtures/sections/gallery.liquid +258 -0
  76. package/fixtures/sections/header.liquid +1157 -0
  77. package/fixtures/sections/html.liquid +40 -0
  78. package/fixtures/sections/image_text.liquid +350 -0
  79. package/fixtures/sections/instagram_plus.liquid +393 -0
  80. package/fixtures/sections/logo_bar.liquid +183 -0
  81. package/fixtures/sections/newsletter.liquid +225 -0
  82. package/fixtures/sections/not_found.liquid +39 -0
  83. package/fixtures/sections/overlay_image.liquid +648 -0
  84. package/fixtures/sections/page_detail.liquid +39 -0
  85. package/fixtures/sections/photo_collection.liquid +433 -0
  86. package/fixtures/sections/product_description.liquid +208 -0
  87. package/fixtures/sections/product_detail.liquid +611 -0
  88. package/fixtures/sections/products.liquid +216 -0
  89. package/fixtures/sections/relative_product.liquid +121 -0
  90. package/fixtures/sections/reviews.liquid +115 -0
  91. package/fixtures/sections/rich_text.liquid +157 -0
  92. package/fixtures/sections/search.liquid +163 -0
  93. package/fixtures/sections/slide.liquid +719 -0
  94. package/fixtures/sections/three_images.liquid +157 -0
  95. package/fixtures/sections/two_images.liquid +125 -0
  96. package/fixtures/sections/video.liquid +95 -0
  97. package/fixtures/sections/video_text.liquid +128 -0
  98. package/fixtures/snippets/bgset.liquid +21 -0
  99. package/fixtures/snippets/card_title.liquid +8 -0
  100. package/fixtures/snippets/cart_modal.liquid +74 -0
  101. package/fixtures/snippets/collection.liquid +77 -0
  102. package/fixtures/snippets/collection_filter_modal.liquid +56 -0
  103. package/fixtures/snippets/default_image_4.liquid +14 -0
  104. package/fixtures/snippets/default_image_6.liquid +18 -0
  105. package/fixtures/snippets/default_image_8.liquid +23 -0
  106. package/fixtures/snippets/four_images_item.liquid +8 -0
  107. package/fixtures/snippets/header_ads.liquid +95 -0
  108. package/fixtures/snippets/hero_image.liquid +94 -0
  109. package/fixtures/snippets/icon_video_play_large.liquid +1 -0
  110. package/fixtures/snippets/icon_video_play_medium.liquid +4 -0
  111. package/fixtures/snippets/icon_video_play_small.liquid +4 -0
  112. package/fixtures/snippets/lazyimg.liquid +22 -0
  113. package/fixtures/snippets/lazyimg_art.liquid +36 -0
  114. package/fixtures/snippets/lazysizes.liquid +41 -0
  115. package/fixtures/snippets/link.liquid +2 -0
  116. package/fixtures/snippets/pagination.liquid +48 -0
  117. package/fixtures/snippets/product.liquid +126 -0
  118. package/fixtures/snippets/product_art_tpl.liquid +152 -0
  119. package/fixtures/snippets/product_info_body.liquid +337 -0
  120. package/fixtures/snippets/product_info_tpl.liquid +423 -0
  121. package/fixtures/snippets/product_label.liquid +46 -0
  122. package/fixtures/snippets/settings.liquid +295 -0
  123. package/fixtures/snippets/social-meta-tags.liquid +106 -0
  124. package/fixtures/snippets/video_html.liquid +11 -0
  125. package/fixtures/snippets/video_source.liquid +98 -0
  126. package/fixtures/snippets/video_thumb_icon.liquid +2 -0
  127. package/fixtures/templates/404.liquid +1 -0
  128. package/fixtures/templates/collection.liquid +92 -0
  129. package/fixtures/templates/index.liquid +206 -0
  130. package/fixtures/templates/page.liquid +1 -0
  131. package/fixtures/templates/product.liquid +99 -0
  132. package/fixtures/templates/search.liquid +1 -0
  133. package/jest.config.js +192 -0
  134. package/lib/__tests__/log.test.js +15 -0
  135. package/lib/__tests__/utils.test.js +69 -0
  136. package/lib/auth/__mocks__/getCode.js +7 -0
  137. package/lib/auth/__mocks__/index.js +0 -0
  138. package/lib/auth/child.js +23 -0
  139. package/lib/auth/getCode.js +35 -0
  140. package/lib/auth/index.js +91 -0
  141. package/lib/commands/__tests__/login.test.js +77 -0
  142. package/lib/commands/__tests__/logout.test.js +29 -0
  143. package/lib/commands/__tests__/store.test.js +44 -0
  144. package/lib/commands/__tests__/switch.test.js +45 -0
  145. package/lib/commands/login.js +99 -0
  146. package/lib/commands/logout.js +14 -0
  147. package/lib/commands/store.js +14 -0
  148. package/lib/commands/switch.js +52 -0
  149. package/lib/commands/theme/__tests__/delete.test.js +49 -0
  150. package/lib/commands/theme/__tests__/init.test.js +21 -0
  151. package/lib/commands/theme/__tests__/list.test.js +80 -0
  152. package/lib/commands/theme/__tests__/package.test.js +17 -0
  153. package/lib/commands/theme/__tests__/publish.test.js +61 -0
  154. package/lib/commands/theme/__tests__/pull.test.js +69 -0
  155. package/lib/commands/theme/__tests__/push.test.js +63 -0
  156. package/lib/commands/theme/__tests__/serve.test.js +107 -0
  157. package/lib/commands/theme/delete.js +64 -0
  158. package/lib/commands/theme/init.js +51 -0
  159. package/lib/commands/theme/list.js +28 -0
  160. package/lib/commands/theme/package.js +37 -0
  161. package/lib/commands/theme/publish.js +56 -0
  162. package/lib/commands/theme/pull.js +62 -0
  163. package/lib/commands/theme/push.js +106 -0
  164. package/lib/commands/theme/serve.js +153 -0
  165. package/lib/commands/theme/share.js +20 -0
  166. package/lib/commands/version.js +6 -0
  167. package/lib/config.js +5 -0
  168. package/lib/db/__mocks__/index.js +9 -0
  169. package/lib/db/__tests__/analytics.test.js +19 -0
  170. package/lib/db/__tests__/user.test.js +20 -0
  171. package/lib/db/analytics.js +48 -0
  172. package/lib/db/index.js +9 -0
  173. package/lib/db/user.js +68 -0
  174. package/lib/log.js +13 -0
  175. package/lib/openAPI/__mocks__/index.js +20 -0
  176. package/lib/openAPI/api.js +76 -0
  177. package/lib/openAPI/index.js +46 -0
  178. package/lib/report.js +37 -0
  179. package/lib/tracing.js +50 -0
  180. package/lib/utils.js +48 -0
  181. package/package.json +54 -0
@@ -0,0 +1,634 @@
1
+ (function ($) {
2
+ $.fn.product_detail = function (opt) {
3
+ var $container = $("#product_detail_" + opt.product.id);
4
+ var $slider = $container.find(".support-slick").eq(0);
5
+ var $thumbs = $container.find('.product-image__thumbs-content');
6
+ var $document = $(document);
7
+ if (opt.product.images.length > 1) {
8
+ $slider.on('init', function () {
9
+ $(this).find('.swiper-slide').removeClass('hidden');
10
+ }).on('beforeChange', function (event, slick, currentSlide, nextSlide) {
11
+ $container.find(".product-image__swiper_bullets").html((nextSlide + 1) + " / " + opt.product.images.length);
12
+ });
13
+ var initSlider = function () {
14
+ $slider.slick({
15
+ lazyLoad: 'ondemand',
16
+ slidesToShow: 1,
17
+ arrows: false,
18
+ adaptiveHeight: true,
19
+ autoplay: false,
20
+ dots: false,
21
+ infinite: true,
22
+ initialSlide: opt.initialSlide || 0,
23
+ touchThreshold: 10,
24
+ waitForAnimate: false,
25
+ useTransform: true,
26
+ rtl: document.documentElement.getAttribute("dir") == "rtl"
27
+ }).on('beforeChange', function (event, slick, currentSlide, nextSlide) {
28
+ $thumbs.find(".slick-current").removeClass("slick-slide slick-current").end().find("[data-thumb-idx=" + nextSlide + "]").addClass("slick-slide slick-current");
29
+ var action = $slider.data("once_action");
30
+ if (action) return;
31
+ if (nextSlide == currentSlide) {
32
+ action = "";
33
+ } else if (nextSlide - currentSlide == 1) {
34
+ action = "next";
35
+ } else if (nextSlide - currentSlide == -1) {
36
+ action = "prev";
37
+ } else if (nextSlide == 0) {
38
+ action = "next";
39
+ } else if (nextSlide == $thumbs.children().length - 1) {
40
+ action = "prev";
41
+ }
42
+ $slider.data("once_action", action);
43
+ nextSlide !== currentSlide && slick.$slides[currentSlide].querySelectorAll('video').forEach(function (video) {
44
+ !video.paused && video.pause();
45
+ });
46
+ }).on("afterChange", function (e, $slick, idx) {
47
+ var $first = $thumbs.find(".product-image__thumbs-item:eq(0)");
48
+ var size = $thumbs.children().length;
49
+ /*$thumbs.find(".slick-current").removeClass("slick-slide slick-current").end().find("[data-thumb-idx=" + idx + "]").addClass("slick-slide slick-current");*/
50
+
51
+ if ($slider.data("once_action")) {
52
+ var action = $slider.data("once_action");
53
+ // lazy load other thumb images
54
+ $thumbs.find(".lazy-lazyload").addClass("lazyload");
55
+ var invisibleThumbs = 0 - (parseInt($first.css("margin-left"), 10) / 80);
56
+ // console.log(action, invisibleThumbs, idx);
57
+ if (action == "next" && idx - invisibleThumbs == 6) {
58
+ $first.css({ marginLeft: '-' + ((invisibleThumbs + 1) * 80) + 'px' });
59
+ }
60
+ if (action == "next" && idx == 0) {
61
+ $first.css({ marginLeft: '0px' });
62
+ }
63
+ if (action == "prev" && idx == (invisibleThumbs - 1)) {
64
+ $first.css({ marginLeft: '-' + ((invisibleThumbs - 1) * 80) + 'px' });
65
+ }
66
+ if (action == "prev" && idx == (size - 1)) {
67
+ $first.css({ marginLeft: '-' + ((size - 6) * 80) + 'px' });
68
+ }
69
+ $slider.data("once_action", false);
70
+ return;
71
+ }
72
+ var marginLeft = 0;
73
+ if (idx < 6) {
74
+ marginLeft = 0;
75
+ } else if (idx > (size - 6) && size > 6) {
76
+ marginLeft = (size - 6) * 80;
77
+ } else {
78
+ marginLeft = idx * 80;
79
+ }
80
+ $first.css({ marginLeft: '-' + marginLeft + 'px' });
81
+ });
82
+ $slider.find(".d-none").removeClass("d-none");
83
+ $slider.find(".product-image__swiper_img").zoom();
84
+ $thumbs.on("mouseenter", "[data-thumb-idx]", function (e) {
85
+ var idx = e.currentTarget.getAttribute("data-thumb-idx");
86
+ $thumbs.find(".slick-current").removeClass("slick-slide slick-current").end().find("[data-thumb-idx=" + idx + "]").addClass("slick-slide slick-current");
87
+ });
88
+ $thumbs.on("mouseenter", "[data-thumb-idx]", $.debounce(function (e) {
89
+ var idx = e.currentTarget.getAttribute("data-thumb-idx");
90
+ $slider.find(".slick-list,.slick-track").stop();
91
+ $slider.data("once_action", "dummy"); // 不需要计算marginLeft
92
+ $slider.slick("slickGoTo", idx, false);
93
+ }, 300));
94
+ $container.on("click", ".swiper-button-prev,.sep-loaded-slider__button-prev", function () {
95
+ var size = $thumbs.children().length;
96
+ var cur = $slider.slick('slickCurrentSlide');
97
+ var newIdx = cur == 0 ? (size - 1) : (cur - 1);
98
+ $slider.data("once_action", "prev").slick("slickGoTo", newIdx, cur == 0);
99
+ });
100
+ $container.on("click", ".swiper-button-next,.sep-loaded-slider__button-next", function () {
101
+ var size = $thumbs.children().length;
102
+ var cur = $slider.slick('slickCurrentSlide');
103
+ var newIdx = (cur == (size - 1)) ? 0 : cur + 1;
104
+ $slider.data("once_action", "next").slick("slickGoTo", newIdx, (cur == (size - 1)));
105
+ });
106
+
107
+ var isDetail = $slider.parents('[data-section-type="product_detail"]').length;
108
+ // track
109
+ if (isDetail) {
110
+ $slider.on('beforeChange', function () {
111
+ $('body').trigger('track', {eventName: 'product_image_slide'})
112
+ });
113
+ $slider.on('click', '.zoom-img', function(e){
114
+ try {
115
+ $(e.target).prev('.product-image__swiper_img').data("zoom-toggle") && $('body').trigger('track', {eventName: 'product_image_zoom'});
116
+ } catch (error) {}
117
+ })
118
+ if ("ontouchstart" in document.body) {
119
+ $slider.on("touchstart", function (e) {
120
+ try {
121
+ e.originalEvent.touches.length > 1 && $('body').trigger('track', {eventName: 'product_image_zoom'});
122
+ } catch (error) {}
123
+ })
124
+ }
125
+ }
126
+ }
127
+ // ajax slick should update until modal visible
128
+ if (opt.ajax) {
129
+ var timer = setInterval(function () {
130
+ if ($('#product-select-modal').length && $('#product-select-modal').is(':visible')) {
131
+ clearInterval(timer);
132
+ initSlider();
133
+ }
134
+ }, 10);
135
+ } else {
136
+ initSlider();
137
+ }
138
+ // 去掉加载过程中的背景
139
+ $container.find(".loading_bg").each(function () {
140
+ if (this.complete) {
141
+ $(this).removeClass("loading_bg");
142
+ } else {
143
+ $(this).one("load", function () {
144
+ $(this).removeClass("loading_bg");
145
+ })
146
+ }
147
+ });
148
+ } else {
149
+ $slider.find("[data-lazy]").each(function () {
150
+ $(this).attr("src", $(this).attr("data-lazy"));
151
+ })
152
+ }
153
+ var variants = opt.product.variants;
154
+ var minPriceVariant = variants.reduce(function (prev, cur) { return parseFloat(prev.price) > parseFloat(cur.price) ? cur : prev }, variants[0]);
155
+ var maxPriceVariant = variants.reduce(function (prev, cur) { return parseFloat(prev.price) < parseFloat(cur.price) ? cur : prev }, variants[0]);
156
+ // 首次拿选择子商品必须从radio中过滤,js 加载前隐藏域的值还没初始化
157
+ var selected = opt.product.options.reduce(function (prev, cur, i) { return prev.filter(function (v) { return v['option' + (i + 1)] == $("[name=option" + (i + 1) + "-" + opt.product.id + "]:checked").val() }) }, variants)[0] || {
158
+ price: parseFloat(minPriceVariant.price),
159
+ price_min: parseFloat(minPriceVariant.price),
160
+ price_max: parseFloat(maxPriceVariant.price),
161
+ compare_at_price: parseFloat(minPriceVariant.compare_at_price),
162
+ off_ratio: parseFloat(minPriceVariant.off_ratio),
163
+ sales: opt.product.sales,
164
+ available_quantity: 999999,
165
+ available: opt.product.available
166
+ };
167
+ $("#selected_variant_id_" + opt.product.id).val(selected.id || "");
168
+ // 兼容
169
+ $document.data("djproduct", {
170
+ product: opt.product,
171
+ selected: selected,
172
+ selectedVariants: selected.id ? [selected] : opt.product.variants,
173
+ qty: parseInt($("#product_quantity_" + opt.product.id).val(), 10),
174
+ element: $container.selector
175
+ });
176
+ $document.trigger('dj.product.variants.change', $document.data('djproduct'));
177
+ $container.data('djproduct', $document.data('djproduct'));
178
+
179
+ }
180
+ $(function () {
181
+ var $document = $(document);
182
+ var $body = $(document.body);
183
+
184
+ var variantsChange = function () {
185
+ // checked
186
+ var productData = $(document).data("djproduct");
187
+ var variants = productData.product.variants.slice();
188
+ var options = productData.product.options;
189
+
190
+ var $cb = $("#" + $(this).attr("for"));
191
+ // checked -> unchecked
192
+ if ($cb.prop("checked")) {
193
+ setTimeout(function () {
194
+ $cb.prop("checked", false);
195
+ }, 10);
196
+ }
197
+ setTimeout(function () { // 等待checked状态变化
198
+ var selectedOpts = [];
199
+ $("#product_detail_" + productData.product.id).find('[name^=option]:checked, option[name^=option]:selected').each(function (i, r) {
200
+ var indexes = r.id.split("-"); // ["option1","0"]
201
+ var optIdx = parseInt(indexes[0].substr(6), 10) - 1;
202
+ var valueIdx = parseInt(indexes[1], 10);
203
+ selectedOpts.push(indexes[0]);
204
+ variants = variants.filter(function (v) { return v[indexes[0]] == options[optIdx].values[valueIdx] });
205
+ });
206
+ $("#selected_variant_id_" + productData.product.id).val(selectedOpts.length == productData.product.options.length ? variants[0].id : "");
207
+ // 重新计算radio disabled状态
208
+ $("#product_detail_" + productData.product.id).find('input.product-info__variants_radio:not(:checked),option.product-info__variants_value:not(:selected)').each(function (i, r) {
209
+ var indexes = r.id.split("-"); // ["option1","0",'id']
210
+ var optIdx = parseInt(indexes[0].substr(6), 10) - 1;
211
+ var valueIdx = parseInt(indexes[1], 10);
212
+ var tmpVariant = $.extend(true, {}, variants[0]);
213
+ tmpVariant[indexes[0]] = options[optIdx].values[valueIdx];
214
+ var tmpSelectedOpts = selectedOpts.slice();
215
+ tmpSelectedOpts.indexOf(indexes[0]) == -1 && (tmpSelectedOpts.push(indexes[0]));
216
+ var disabled = productData.product.variants.filter(function (v) {
217
+ return (v.option1 == tmpVariant.option1 || tmpSelectedOpts.indexOf('option1') === -1) && (v.option2 == tmpVariant.option2 || tmpSelectedOpts.indexOf('option2') === -1) && (v.option3 == tmpVariant.option3 || tmpSelectedOpts.indexOf('option3') === -1) && v.available_quantity > 0;
218
+ }).length == 0;
219
+ $(r).prop("disabled", disabled);
220
+ $(r).html($(r).val());
221
+ $(r).parent('select').val() && disabled && $(r).html($(r).val() + ' (' + $(r).data('soldout') + ')');
222
+ $(r).parent('select').val() && $(r).parent('select').removeClass('tw-text-black tw-opacity-50')
223
+ })
224
+ var minPriceVariant = variants.reduce(function (prev, cur) { return parseFloat(prev.price) > parseFloat(cur.price) ? cur : prev }, variants[0]);
225
+ var maxPriceVariant = variants.reduce(function (prev, cur) { return parseFloat(prev.price) < parseFloat(cur.price) ? cur : prev }, variants[0]);
226
+ productData.selected = (selectedOpts.length == productData.product.options.length ? variants[0] : {
227
+ price: parseFloat(minPriceVariant.price),
228
+ price_min: parseFloat(minPriceVariant.price),
229
+ price_max: parseFloat(maxPriceVariant.price),
230
+ compare_at_price: parseFloat(minPriceVariant.compare_at_price),
231
+ off_ratio: parseFloat(minPriceVariant.off_ratio),
232
+ sales: productData.product.sales,
233
+ available_quantity: 999999,
234
+ available: productData.product.available
235
+ });
236
+ productData.selectedVariants = variants;
237
+ productData.qty = Math.min(parseInt($("#product_quantity_" + productData.product.id).val(), 10), productData.selected.available_quantity);
238
+ $(document).data("djproduct", productData);
239
+ $(document).trigger('dj.product.variants.change', productData);
240
+ // 色卡label
241
+ !$cb.prop('disabled') && $cb.parents('.product-info__variants_items').find('#variant_color-label').html($cb.prop("checked") ? ' - ' + $cb.val() : '');
242
+
243
+ if (productData.selectedVariants.length === 1) {
244
+ // quickview 中不更新url
245
+ if ($('#product-select-modal.show').length) return;
246
+ window.history.replaceState(null, '', '?' + $.toQuery($.extend($.params(), {
247
+ variant: productData.selectedVariants[0].id
248
+ })))
249
+ }
250
+ }, 20);
251
+ }
252
+ //子商品切换
253
+ $body.on("click", ".product-info__variants_value label", function () {
254
+ variantsChange.call(this);
255
+ }).on('change', '.product-info__variants_items select', function () {
256
+ variantsChange.call(this);
257
+ });
258
+ // +/-
259
+ $body.on('click', "[data-click=decrease],[data-click=increase]", function (e) {
260
+ var t = $(e.target).attr("data-click");
261
+ var productData = $document.data("djproduct");
262
+ productData.qty = productData.qty || 1;
263
+ productData.qty += (t == "decrease" ? -1 : 1);
264
+ $document.data("djproduct", productData);
265
+ $document.trigger('dj.product.variants.change', $document.data('djproduct'));
266
+ })
267
+ // input product_quantity
268
+ $body.on('blur', ".product-info__qty_num", function (e) {
269
+ var productData = $document.data("djproduct");
270
+ var correctedValue = Math.max(Math.min(parseInt($(this).val().replace(/\D/g, "").replace(/^0*/, ""), 10) || 0, productData.selected.available_quantity || productData.product.available_quantity), 1);
271
+ productData.qty = parseInt(correctedValue, 10);
272
+ $document.data("djproduct", productData);
273
+ $document.trigger('dj.product.variants.change', $document.data('djproduct'));
274
+
275
+ })
276
+
277
+ // slider switch
278
+ $document.on('dj.product.variants.change', function (e, data) {
279
+ if (!data.selected.image) return;
280
+ var $container = $("#product_detail_" + data.product.id);
281
+ // 兼容公共卡片
282
+ if (!$container.data('life-style')) return;
283
+
284
+ var $slider = $container.find('.support-slick').eq(0);
285
+ var idx = data.product.images.findIndex(function (row) {
286
+ return row.src == data.selected.image.src;
287
+ });
288
+
289
+ $slider.hasClass('slick-initialized') && $slider.slick('slickGoTo', idx);
290
+ });
291
+
292
+ // update discount label
293
+ $document.on('dj.product.variants.change', function (e, data) {
294
+ // 更新折扣标签和优惠金额
295
+ var off = window.SHOP_PARAMS.product_lang.off.replace(/\{\s*count\s*\}/, data.selected.off_ratio)
296
+ var $productImage = $("#product_detail_" + data.product.id + ' .product-image');
297
+ if (data.selected.off_ratio > 0 && data.selected.available) {
298
+ var save_html = window.SHOP_PARAMS.product_lang.save_html.replace(/\{\s*saved_amount\s*\}/, (data.selected.compare_at_price - data.selected.price).toFixed(2))
299
+ $productImage.find('.product-info__discount-label').html(off).end().find('.product-info__save-label').html(save_html);
300
+ $productImage.find('.product-info__label').css('opacity', 1);
301
+ } else {
302
+ $productImage.find('.product-info__label').css('opacity', 0);
303
+ }
304
+ });
305
+
306
+ // qty section sku
307
+ $document.on('dj.product.variants.change', function (e, data) {
308
+ $("#product_detail_" + data.product.id + " .product-info__qty_container").html(template("product-info-qty-tpl", { product: $.extend({}, data.product, data.selected, { id: data.product.id }), qty: data.qty }));
309
+ $("#product_detail_" + data.product.id + " .product-info__qty_stock")[data.selectedVariants.length > 1 ? "hide" : "show"]();
310
+ $("#product_detail_" + data.product.id + " .product-info__header-sku")[data.selectedVariants.length > 1 ? "hide" : "show"]().html(data.selected.sku);
311
+ });
312
+
313
+ // price section
314
+ $document.on('dj.product.variants.change', function (e, data) {
315
+ $("#product_detail_" + data.product.id + " .product-info__header_price-wrapper").html(template("product-info-price-wrapper", {
316
+ product: data.selected
317
+ }));
318
+ });
319
+
320
+ //描述 tab
321
+ $(function(){
322
+ $(document).off('change.descTab').on('change.descTab', '.product-info__desc-tab-cb', function(){
323
+ var checked = $(this).prop('checked');
324
+ var $content = $(this).next();
325
+ $(this).parents('.product-info__desc-wrap').toggleClass('is-open', checked);
326
+ checked
327
+ ? $content.slideDown(300)
328
+ : $content.slideUp(300);
329
+ })
330
+ })
331
+
332
+ // 获取自定义参数
333
+ function getProperties() {
334
+ var productData = $document.data("djproduct");
335
+ var properties = {};
336
+ var productId = productData.product.id;
337
+ var $properties = $(".product-info-" + productId);
338
+ var formdata = $properties.serializeArray();
339
+ for (var i = 0; i < formdata.length; i++) {
340
+ var result = /properties\[(.+)\]/g.exec(formdata[i].name);
341
+ if (result && result[1]) {
342
+ properties[result[1]] = formdata[i].value;
343
+ }
344
+ }
345
+ var items = $properties.find('.required');
346
+ var valid = true;
347
+ for (i = 0; i < items.length; i++) {
348
+ var $parent = $(items[i]).parents('.line-item-property__field');
349
+ $parent.find('.not-empty').remove();
350
+ if ($(items[i]).val() == '') {
351
+ $(items[i]).addClass('not-empty-field');
352
+ $parent.append('<div class="not-empty" style="color: #ea0000;">can not be empty</div>');
353
+ valid = false;
354
+ } else {
355
+ $parent.removeClass('not-empty-field');
356
+ }
357
+ }
358
+ return valid ? properties : false;
359
+ }
360
+ // add to cart
361
+ $(document).on("dj.common.product.atc", function (e, options) {
362
+ var properties = options.properties || {};
363
+ $.loading.show();
364
+ $.post("/api/cart", {
365
+ product_id: options.product_id,
366
+ variant_id: options.variant_id,
367
+ quantity: options.quantity,
368
+ properties: properties,
369
+ }).always(function (res) {
370
+ var data = res.readyState ? res.responseJSON : res;
371
+ //addToCart按钮可以选择流程:1)to_cart: 跳到购物车 2)to_checkout: 跳到支付页 3)to_toast:弹提示框,页面不跳转
372
+ var process = options.process;
373
+ if (data.state === "success") {
374
+ $(document.body).trigger("dj.addToCart", {
375
+ id: options.product_id,
376
+ number: options.quantity,
377
+ childrenId: options.variant.id,
378
+ item_price: options.variant.price,
379
+ name: options.product.title,
380
+ type: options.variant.type ? options.variant.type : options.product.type,
381
+ properties: properties,
382
+ quantity: options.quantity,
383
+ variant_id: options.variant.id,
384
+ product_id: options.product_id,
385
+ product: options.product,
386
+ variant: options.variant,
387
+ source: options.source,
388
+ process: options.process
389
+ })
390
+
391
+ if (process == 'to_cart') {
392
+ $.loading.hide();
393
+ location.href = '/cart';
394
+ return void 0;
395
+ } else if (process == 'to_checkout') {
396
+ $.loading.hide();
397
+ $(document).trigger("dj.common.product.buy_now", options);
398
+ } else if (process == 'toast') {
399
+ $.loading.hide();
400
+ $.toast.show({ content: window.SHOP_PARAMS.product_lang.added_to_cart_successfully });
401
+ } else if (process == 'to_toast') {
402
+ // 购物车弹窗
403
+ if (window.SHOP_PARAMS.template_type == '13') { $.loading.hide(); }
404
+ }
405
+ $(document).trigger('dj.common.cart.change')
406
+ } else {
407
+ $.loading.hide();
408
+ $.toast.show({ content: data.message || 'Unknown error', type: 'error' });
409
+ }
410
+ })
411
+
412
+ })
413
+ $body.on("click", "[data-click=addToCart]", function (e) {
414
+ var properties = getProperties();
415
+ if (!properties) return;
416
+ var productData = $document.data("djproduct");
417
+ var values = $.params("?" + $(e.target).parents(".product-info").serialize());
418
+ if (!values.variant_id) {
419
+ $.toast.show({ type: 'error', content: window.SHOP_PARAMS.product_lang.select_variant });
420
+ return;
421
+ }
422
+ values.properties = properties;
423
+ values.process = (window.SHOP_PARAMS.product_settings || {}).add_to_cart_process;
424
+ values.product = productData.product;
425
+ values.variant = productData.selected;
426
+ values.quantity = productData.qty || 1;
427
+ $(document).trigger("dj.common.product.atc", values);
428
+ })
429
+ // buy now
430
+ $(document).on("dj.common.product.buy_now", function (e, options) {
431
+ var properties = options.properties || {};
432
+ $.loading.show();
433
+ $.post('/api/checkout/order', {
434
+ line_items: [{
435
+ quantity: options.quantity,
436
+ variant_id: options.variant_id,
437
+ note: '',
438
+ properties: properties,
439
+ }],
440
+ refer_info: {
441
+ source: 'buy_now'
442
+ }
443
+ }, function (ret) {
444
+ $.loading.hide();
445
+ if (ret.state === 'success') {
446
+ return (location.href = '/checkout/' + ret.data.order_token + '?step=contact_information');
447
+ } else {
448
+ $.toast.show({
449
+ content:
450
+ {
451
+ '30003': "Some items have been removed, please re-order",
452
+ 'line_items_variant_withdraw': "Some items have been sold out, please re-order",
453
+ 'line_items_variant_sold_out': "Some items are not in stock, please re-order"
454
+ }[ret.state] || ret.errors[0] || "Some items information has been updated, please re-order"
455
+ });
456
+ }
457
+
458
+ });
459
+
460
+ })
461
+ $body.on("click", "[data-click=submit]", function (e) {
462
+ var values = $.params("?" + $(e.target).parents(".product-info").serialize());
463
+ if (!values.variant_id) {
464
+ $.toast.show({ type: 'error', content: window.SHOP_PARAMS.product_lang.select_variant });
465
+ return;
466
+ }
467
+ var properties = getProperties();
468
+ if (!properties) return;
469
+ values.properties = properties;
470
+ $(document).trigger("dj.common.product.buy_now", values);
471
+ })
472
+
473
+ // quickview
474
+ $(document).on('dj.common.product.select', function (e, options) {
475
+ // quick view弹窗
476
+ var originData = $(document).data('djproduct');
477
+ $(document).on('hide.bs.modal', '#product-select-modal', function () {
478
+ $(document).data('djproduct', originData);
479
+ });
480
+ $.loading.show();
481
+ var is_select_default_variants = window.SHOP_PARAMS.is_select_default_variants || false;
482
+ $.get("/api/products/" + options.id, function (res) {
483
+ $.loading.hide();
484
+ $('#product-select-modal').remove();
485
+ var product = res && res.data && res.data.product;
486
+ if (!product) return;
487
+ var selectedVariantId = "";
488
+ var selectedVariant = "";
489
+ var priceMin = 99999999;
490
+ var comparePriceMin = 99999999;
491
+ var priceMax = 0;
492
+ if (is_select_default_variants || product.variants.size == 1) {
493
+ for (var i = 0; i < product.variants.length; i++) {
494
+ var variant = product.variants[i];
495
+ if (variant.available_quantity > 0) {
496
+ selectedVariantId = variant.id;
497
+ break;
498
+ }
499
+ }
500
+ }
501
+ for (var i = 0; i < product.variants.length; i++) {
502
+ var variant = product.variants[i];
503
+ if (variant.compare_at_price < comparePriceMin) {
504
+ comparePriceMin = variant.compare_at_price;
505
+ }
506
+ if (variant.price < priceMin) {
507
+ priceMin = variant.price;
508
+ }
509
+ if (variant.price > priceMax) {
510
+ priceMax = variant.price;
511
+ }
512
+ if (variant.id == selectedVariantId) {
513
+ selectedVariant = variant;
514
+ }
515
+ }
516
+ var initialSlide = 0;
517
+ if (selectedVariant && selectedVariant.image && selectedVariant.image.src) {
518
+ for (var i = 0; i < product.images.length; i++) {
519
+ if (product.images[i] == selectedVariant.image.src ) {
520
+ initialSlide = i;
521
+ }
522
+ }
523
+ }
524
+ $('body').prepend(template('product-select-wrapper', {product: product, initialSlide: initialSlide, selectedVariantId: selectedVariantId, selectedVariant: selectedVariant, priceMin: +priceMin, comparePriceMin: +comparePriceMin, priceMax: +priceMax}));
525
+ if (window.innerWidth <= 768) {
526
+ $('#product-select-modal').removeClass("fade");
527
+ }
528
+ $('#product-select-modal').modal('show');
529
+ $("#product-select-modal .product-detail").product_detail({product: product, initialSlide: initialSlide, ajax: true });
530
+ $(document).trigger('plugin_currency_update');
531
+ });
532
+ e.stopPropagation();
533
+ e.preventDefault();
534
+ return false;
535
+ });
536
+ // quick view detail url
537
+ $document.on('dj.product.variants.change', function (e, data) {
538
+ var selectedVariantId = data.selectedVariants.length > 1 ? "" : ((data.selected && data.selected.id) || "");
539
+ var $link = $("#product_detail_" + data.product.id + " .product-info__url a");
540
+ if (!$link.length) return;
541
+ var href = $link.attr("href").replace(/&*variant=[a-z0-9-]*/, "");
542
+ $link.attr("href", href + (selectedVariantId ? ((href.indexOf("?") == -1 ? "?" : "&") + "variant=" + selectedVariantId) : ""));
543
+ });
544
+ });
545
+ })(window.jQuery);
546
+ // 自定义款式 图片上传
547
+ $(function () {
548
+ $('.line-item-property__field input[type="file"]').val('');
549
+ $(document).on('change', '.line-item-property__field input[type="file"]', function () {
550
+ var id = $(this).attr('id');
551
+ var file = $(this)[0].files[0];
552
+ file || $('img[data-name="' + id + '"]').attr('src', "").hide();
553
+ if (id && file) {
554
+ if (file.size > (parseInt($("#" + id).data("max-size"), 10) || 10485760)) {
555
+ $('[data-name="' + id + '"],#' + id).val("");
556
+ $('img[data-name="' + id + '"]').attr('src', "").hide();
557
+ return $.toast.show({ content: $("#" + id).data("max-size-msg") || "File size exceeds 10MB, please change to a smaller one" });
558
+ }
559
+ uploadImage(id, file);
560
+ } else {
561
+ $('[data-name="' + id + '"]').val("");
562
+ }
563
+ });
564
+ /**
565
+ * upload image
566
+ */
567
+ function uploadImage(id, file) {
568
+ var objectName = window.SHOP_PARAMS.shop_id + "/" + (new Date()).getTime() + "." + file.name.split(".").pop();
569
+ $("#" + id).css({ "pointer-events": "none" });
570
+ $.getJSON("/api/file/s3-sign?key=" + objectName).then(function (signData) {
571
+ fetch(signData.write_host, {
572
+ method: 'put',
573
+ mode: 'cors',
574
+ body: file,
575
+ headers: { "Content-Type": file.type }
576
+ }).then(function () {
577
+ var url = signData.read_host + objectName;
578
+ $("#" + id).css({ "pointer-events": "" });
579
+ $('[data-name="' + id + '"]').val(url);
580
+ $('img[data-name="' + id + '"]').attr('src', url).show();
581
+ // 修改为已上传
582
+ $('[data-name="' + id + '"]').parent('label');
583
+ $('[data-name="' + id + '"]').parent('.line-item-property__field').find('.not-empty').remove();
584
+ }).catch(function () {
585
+ $("#" + id).css({ "pointer-events": "" });
586
+ $.toast.show({ content: $("#" + id).data("error-msg") || "Upload error, please try again" });
587
+ $('[data-name="' + id + '"],#' + id).val("");
588
+ $('img[data-name="' + id + '"]').attr('src', "").hide();
589
+ });
590
+ });
591
+ }
592
+
593
+ $(document).on('click', '.line-item-property__field .item', function () {
594
+ $(this).parents('.line-item-property__field').find('.item').removeClass('selected');
595
+ $(this).parents('.line-item-property__field').find('.item_value').html($(this).find('input').val());
596
+ $(this).addClass('selected');
597
+ })
598
+
599
+ $(document).on('blur', '.line-item-property__field .required', function () {
600
+ if ($(this).val()) {
601
+ var $parent = $(this).parents('.line-item-property__field');
602
+ $parent.find('.required').removeClass('not-empty-field');
603
+ $parent.find('.not-empty').remove();
604
+ }
605
+ })
606
+ $('.line-item-property__field').each(function () {
607
+ $(this).find('.item:first').click();
608
+ })
609
+ });
610
+ /** new product detail ends */
611
+
612
+ $(function(){
613
+ if(window.SHOP_PARAMS && window.SHOP_PARAMS.template_type == '1'){
614
+ $(document).on(
615
+ 'scroll.view_page_tail',
616
+ $.throttle(
617
+ function () {
618
+ try {
619
+ var scrollTop = $(document).scrollTop(); //滚动条距离顶部的高度
620
+ var clientHeight = $(window).height(); //当前可视的页面高度
621
+ var scrollHeight = $(document).height(); //当前页面的总高度
622
+ //判断是否滑动到底部
623
+ if(scrollHeight - clientHeight <= scrollTop + 10){
624
+ $(document.body).trigger('track', { eventName: 'view_page_tail' })
625
+ $(document).off('scroll.view_page_tail')
626
+ }
627
+ } catch (error) {}
628
+ },
629
+ 200,
630
+ 50
631
+ )
632
+ );
633
+ }
634
+ });