1/** 2 * ÐвÑопÑименение пÑомокода ÑолÑко поÑле REST-пÑовеÑки: код ÑÑÑеÑÑвÑÐµÑ Ð² Woo и еÑÑ Ð½Ðµ в коÑзине. 3 * Blur/change не иÑполÑзÑем (Ð½ÐµÑ Ñпама apply_coupon пÑи набоÑе). 4 */ 5(function ($) { 6 'use strict'; 7 8 /** 9 * Ðод пÑомокода в ÑекÑÑе Ñведомлений (в кавÑÑÐºÐ°Ñ ) â заглавнÑми, как в поле ввода. 10 * ÐÑблиÑÑÐµÑ PHP-ÑилÑÑÑÑ: CCB вÑÑавлÑÐµÑ HTML Ñ Ð¾ÑвеÑа AJAX, иногда Ð¾Ð±Ñ Ð¾Ð´Ñ ÑеÑвеÑнÑÑ Ð½Ð¾ÑмализаÑиÑ. 11 */ 12 function uppercasePromoCodeInsideQuotes(text) { 13 if (!text || typeof text !== 'string') { 14 return text; 15 } 16 return text 17 .replace(/"([A-Za-z0-9_-]+)"/g, function (_, c) { 18 return '"' + c.toUpperCase() + '"'; 19 }) 20 .replace(/«([A-Za-z0-9_-]+)»/g, function (_, c) { 21 return '«' + c.toUpperCase() + '»'; 22 }) 23 .replace(/\u201C([A-Za-z0-9_-]+)\u201D/g, function (_, c) { 24 return '\u201C' + c.toUpperCase() + '\u201D'; 25 }) 26 .replace(/"([A-Za-z0-9_-]+)"/g, function (_, c) { 27 return '"' + c.toUpperCase() + '"'; 28 }); 29 } 30 31 function uppercaseCouponNoticeTextNodes(root) { 32 var scope = root && root.querySelectorAll ? root : document; 33 var sel = 34 '.cart-coupon-wrap.coupon-box .woocommerce-error li, .cart-coupon-wrap.coupon-box .woocommerce-message li, .cart-coupon-wrap.coupon-box .woocommerce-info li, .ccb-checkout-wrapper ul.woocommerce-error li, .ccb-checkout-wrapper ul.woocommerce-message li'; 35 var lis = scope.querySelectorAll(sel); 36 var j, li, walk, node, next; 37 for (j = 0; j < lis.length; j++) { 38 li = lis[j]; 39 walk = document.createTreeWalker(li, NodeFilter.SHOW_TEXT, null); 40 while ((node = walk.nextNode())) { 41 next = uppercasePromoCodeInsideQuotes(node.nodeValue); 42 if (next !== node.nodeValue) { 43 node.nodeValue = next; 44 } 45 } 46 } 47 } 48 49 function getConfig() { 50 var c = window.bonoChildCouponCheck; 51 if (!c || !c.restUrl) { 52 return { restUrl: '', nonce: '', minLength: 3, debounceMs: 800 }; 53 } 54 return { 55 restUrl: c.restUrl, 56 nonce: c.nonce || '', 57 minLength: typeof c.minLength === 'number' ? c.minLength : 3, 58 debounceMs: typeof c.debounceMs === 'number' ? c.debounceMs : 800 59 }; 60 } 61 62 function normalize(code) { 63 return String(code || '').trim(); 64 } 65 66 function pickApplyButton($input) { 67 var $wrap = $input.closest('.cart-coupon-wrap.coupon-box'); 68 if ($wrap.length) { 69 var $ccbBtn = $wrap.find('button[data-ccb-apply-coupon="1"]').first(); 70 if ($ccbBtn.length) { 71 return $ccbBtn; 72 } 73 } 74 75 var $form = $input.closest('form'); 76 if ($form.length) { 77 var $btn = $form.find('button[name="apply_coupon"], button[type="submit"]').first(); 78 if ($btn.length) { 79 return $btn; 80 } 81 } 82 83 return $('button[data-ccb-apply-coupon="1"], form.checkout_coupon button[name="apply_coupon"], form.checkout_coupon button[type="submit"]').first(); 84 } 85 86 function clearDebounceTimer($input) { 87 var t = $input.data('vbAutoApplyTimer'); 88 if (t) { 89 window.clearTimeout(t); 90 } 91 $input.removeData('vbAutoApplyTimer'); 92 } 93 94 function abortFetch($input) { 95 var prev = $input.data('vbCouponCheckAbort'); 96 if (prev && typeof prev.abort === 'function') { 97 prev.abort(); 98 } 99 } 100 101 /** 102 * Ð¡Ð¸Ð½Ñ ÑонизаÑÐ¸Ñ data-vb-last-auto-applied Ñ ÑакÑом коÑÐ·Ð¸Ð½Ñ Ð¿Ð¾Ñле updated_checkout / updated_cart_totals. 103 */ 104 function syncInputWithCart($input) { 105 var cfg = getConfig(); 106 if (!cfg.restUrl || !cfg.nonce) { 107 return; 108 } 109 var code = normalize($input.val()); 110 if (!code) { 111 $input.removeAttr('data-vb-last-auto-applied'); 112 return; 113 } 114 115 abortFetch($input); 116 var ac = new window.AbortController(); 117 $input.data('vbCouponCheckAbort', ac); 118 119 var url = cfg.restUrl + (cfg.restUrl.indexOf('?') >= 0 ? '&' : '?') + 'code=' + encodeURIComponent(code); 120 121 window 122 .fetch(url, { 123 method: 'GET', 124 credentials: 'same-origin', 125 signal: ac.signal, 126 headers: { 127 'X-WP-Nonce': cfg.nonce 128 } 129 }) 130 .then(function (r) { 131 if (!r.ok) { 132 return null; 133 } 134 return r.json(); 135 }) 136 .then(function (data) { 137 if (!data || typeof data !== 'object') { 138 return; 139 } 140 if (data.already_applied) { 141 $input.attr('data-vb-last-auto-applied', code); 142 } else { 143 $input.removeAttr('data-vb-last-auto-applied'); 144 } 145 }) 146 .catch(function () { 147 /* ignore abort */ 148 }); 149 } 150 151 function onCouponPrecheckDone($input, code, data) { 152 if (!data) { 153 return; 154 } 155 156 if (!data.exists) { 157 $input.attr('data-vb-fail-code', code); 158 return; 159 } 160 161 if (data.already_applied) { 162 $input.attr('data-vb-last-auto-applied', code); 163 return; 164 } 165 166 var $btn = pickApplyButton($input); 167 if (!$btn || !$btn.length) { 168 return; 169 } 170 if ($btn.closest('.processing').length) { 171 return; 172 } 173 174 $input.attr('data-vb-last-auto-applied', code); 175 $btn.trigger('click'); 176 } 177 178 function runPrecheck($input) { 179 var cfg = getConfig(); 180 if (!cfg.restUrl || !cfg.nonce) { 181 return; 182 } 183 184 var code = normalize($input.val()); 185 if (code.length < cfg.minLength) { 186 return; 187 } 188 189 var fail = $input.attr('data-vb-fail-code'); 190 if (fail !== undefined && code === fail) { 191 return; 192 } 193 194 var last = normalize($input.attr('data-vb-last-auto-applied')); 195 if (last && code === last) { 196 return; 197 } 198 199 abortFetch($input); 200 var ac = new window.AbortController(); 201 $input.data('vbCouponCheckAbort', ac); 202 203 var url = cfg.restUrl + (cfg.restUrl.indexOf('?') >= 0 ? '&' : '?') + 'code=' + encodeURIComponent(code); 204 205 window 206 .fetch(url, { 207 method: 'GET', 208 credentials: 'same-origin', 209 signal: ac.signal, 210 headers: { 211 'X-WP-Nonce': cfg.nonce 212 } 213 }) 214 .then(function (r) { 215 if (!r.ok) { 216 return null; 217 } 218 return r.json(); 219 }) 220 .then(function (data) { 221 onCouponPrecheckDone($input, code, data); 222 }) 223 .catch(function (err) { 224 if (err && err.name === 'AbortError') { 225 return; 226 } 227 }); 228 } 229 230 function onInputImmediate($input) { 231 var v = normalize($input.val()); 232 var fail = $input.attr('data-vb-fail-code'); 233 if (fail !== undefined && v !== fail) { 234 $input.removeAttr('data-vb-fail-code'); 235 } 236 var lap = $input.attr('data-vb-last-auto-applied');
237 if (lap !== undefined && v !== lap) { 238 $input.removeAttr('data-vb-last-auto-applied'); 239 } 240 } 241 242 function buildDebouncedHandler() { 243 var cfg = getConfig(); 244 var wait = cfg.debounceMs; 245 246 return function (e) { 247 var el = e && e.target ? e.target : null; 248 if (!el || el.tagName !== 'INPUT' || el.name !== 'coupon_code') { 249 return; 250 } 251 var $input = $(el); 252 onInputImmediate($input); 253 254 clearDebounceTimer($input); 255 var t = window.setTimeout(function () { 256 runPrecheck($input); 257 }, wait); 258 $input.data('vbAutoApplyTimer', t); 259 }; 260 } 261 262 $(function () { 263 uppercaseCouponNoticeTextNodes(document); 264 265 $(document.body).on('input', 'input[name="coupon_code"]', buildDebouncedHandler()); 266 267 $(document.body).on('updated_checkout updated_cart_totals', function () { 268 $('input[name="coupon_code"]').each(function () { 269 var $i = $(this); 270 clearDebounceTimer($i); 271 syncInputWithCart($i); 272 }); 273 window.setTimeout(function () { 274 uppercaseCouponNoticeTextNodes(document); 275 }, 0); 276 }); 277 278 $(document.body).on('applied_coupon_in_checkout', function () { 279 window.setTimeout(function () { 280 uppercaseCouponNoticeTextNodes(document); 281 }, 0); 282 }); 283 284 // ÐÑÐ²ÐµÑ apply_coupon вÑÑавлÑеÑÑÑ Ð² DOM без обÑзаÑелÑного updated_checkout в Ñом же Ñике. 285 if (typeof MutationObserver !== 'undefined') { 286 var moTimer = null; 287 var mo = new MutationObserver(function () { 288 if (moTimer) { 289 window.clearTimeout(moTimer); 290 } 291 moTimer = window.setTimeout(function () { 292 uppercaseCouponNoticeTextNodes(document); 293 }, 50); 294 }); 295 var wrap = document.querySelector('.ccb-checkout-wrapper'); 296 if (wrap) { 297 mo.observe(wrap, { childList: true, subtree: true }); 298 } 299 } 300 }); 301})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.