PageSourceSearch

https://www.merrittpress.com/Scripts/pricing.store.js?v=2026.10.1.2

js merrittpress.com collected 2026-10-09 22:36:35 UTC 20,237 bytes, 504 lines download raw bytes

1// Add an URL parser to JQuery that returns an object
2// This function is meant to be used with an URL like the window.location
3// Simple variable:  ?var=abc                        returns {var: "abc"}
4// Simple object:    ?var.length=2&var.scope=123     returns {var: {length: "2", scope: "123"}}
5// Simple array:     ?var[]=0&var[]=9                returns {var: ["0", "9"]}
6// Array with index: ?var[0]=0&var[1]=9              returns {var: ["0", "9"]}
7// Nested objects:   ?my.var.is.here=5               returns {my: {var: {is: {here: "5"}}}}
8// All together:     ?var=a&my.var[]=b&my.cookie=no  returns {var: "a", my: {var: ["b"], cookie: "no"}}
9// You just cant have an object in an array, e.g. ?var[1].test=abc DOES NOT WORK
10
11function alParseParams(query) {
12    var re = /([^&=]+)=?([^&]*)/g;
13    var decode = function (str) {
14        return decodeURIComponent(str.replace(/\+/g, ' '));
15    };
16
17    // recursive function to construct the result object
18    function createElement(params, key, value, shouldPush) {
19        key = key + '';
20
21        // if the key is a property
22        if (key.indexOf('.') !== -1 && (key.indexOf('[') == -1 || (key.indexOf('[') > key.indexOf('.')))) {
23            // extract the first part with the name of the object
24            var list = key.split('.');
25
26            // the rest of the key
27            var new_key = key.split(/\.(.+)?/)[1];
28
29            // create the object if it doesnt exist
30            if (!params[list[0]]) params[list[0]] = {};
31
32            // if the key is not empty, create it in the object
33            if (new_key !== '') {
34                createElement(params[list[0]], new_key, value);
35            } else console.warn('parseParams :: empty property in key "' + key + '"', false);
36        } else
37            // if the key is an array
38            if (key.indexOf('[') !== -1) {
39                // extract the array name
40                var list = key.split('[');
41                key = list[0];
42
43                // extract the index of the array
44                var list = list[1].split(']');
45                var index = list[0]
46
47                // if index is empty, just push the value at the end of the array
48                if (index == '') {
49                    if (!params) params = {};
50                    if (!params[key] || !$.isArray(params[key])) params[key] = [];
51                    params[key].push(value);
52                } else
53                // add the value at the index (must be an integer)
54                {
55                    if (!params) params = {};
56                    if (!params[key] || !$.isArray(params[key])) params[key] = [];
57
58                    if (list[1].indexOf('.') !== -1) {
59                        var new_key = list[1].split(/\.(.+)?/)[1];
60                        // if the key is not empty, create it in the object
61                        if (new_key !== '') {
62                            if (!params[key][parseInt(index)] || (!$.isArray(params[key][parseInt(index)]) && !$.isPlainObject(params[key][parseInt(index)]))) {
63                                if (new_key.indexOf('.') !== -1 || new_key.indexOf('[') !== -1) {
64                                    params[key][parseInt(index)] = [];
65                                } else {
66                                    params[key][parseInt(index)] = {};
67                                }
68                            }
69
70                            if (new_key.indexOf('.') !== -1 || new_key.indexOf('[') !== -1)
71                                createElement(params[key][parseInt(index)], new_key, value, true);
72                            else {
73                                if (params[key][parseInt(index)][new_key])
74                                    params[key][parseInt(index)][new_key] += ';' + value;
75                                else
76                                    params[key][parseInt(index)][new_key] = value;
77                            }
78                        } else console.warn('parseParams :: empty property in key "' + list[1] + '"');
79                    } else params[key][parseInt(index)] = value;
80                }
81            } else
82            // just normal key
83            {
84                if (!params) params = {};
85                if (!shouldPush) {
86                    params[key] = value;
87                } else {
88                    var nestedParam = {};
89                    params.push(nestedParam);
90                    createElement(nestedParam, key, value, false);
91                }
92            }
93    }
94
95    // be sure the query is a string
96    query = query + '';
97
98    if (query === '') query = window.location + '';
99
100    var params = {}, e;
101    if (query) {
102        // remove # from end of query
103        if (query.indexOf('#') !== -1) {
104            query = query.substr(0, query.indexOf('#'));
105        }
106
107        // remove ? at the begining of the query
108        if (query.indexOf('?') !== -1) {
109            query = query.substr(query.indexOf('?') + 1, query.length);
110        }
111
112        // empty parameters
113        if (query == '') return {};
114
115        // execute a createElement on every key and value
116        while (e = re.exec(query)) {
117            var key = decode(e[1]);
118            var value = decode(e[2]);
119            createElement(params, key, value, false);
120        }
121    }
122    return params;
123}
124
125//to be run on engine request start
126function requestStart(sender, eventArgs) {
127  console.log("requestStart");
128  kendo.ui.progress($('#pricingArea'), true);
129  if (window.calcStart) {
130    calcStart(sender, eventArgs);
131  }
132  if (window.intCalcStart) {
133    intCalcStart(sender, eventArgs);
134  }
135  $("#detailPage_shippingCalculatorResults").html("");
136}
137//to be run on engine request end
138function responseEnd(sender, eventArgs) {
139  console.log("responseEnd");
140  kendo.ui.progress($('#pricingArea'), false);
141  if (window.calcFinish) {
142    calcFinish(sender, eventArgs);
143  }
144  if (window.intCalcFinish) {
145    intCalcFinish(sender, eventArgs);
146  }
147}
148
149function ResetCalculatorDropdownSelection(pricingParams) {
150    if (pricingParams && pricingParams.Options) {
151        $(pricingParams.Options).each(function (index) {
152            if(pricingParams.Options[index])
153            {
154                $("#Options\\[" + index + "\\]\\.Value").val(pricingParams.Options[index].Value);
155            }
156        });
157  }
158}
159
160function extractNumber(obj, decimalPlaces, allowNegative, separator) {
161  "use strict";
162
163  //Assuming '.' as the default separator
164  if (!separator)
165    separator = '.';
166
167  //will prevent anything other than a number entered withing min and max payment value text boxes
168  var temp = obj.value;
169  // avoid changing things if already formatted correctly
170  var reg0Str = '[0-9]*';
171  if (decimalPlaces > 0) {
172    reg0Str += '\\' + separator + '?[0-9]{0,' + decimalPlaces + '}';
173  } else if (decimalPlaces < 0) {
174    reg0Str += '\\' + separator + '?[0-9]*';
175  }
176  reg0Str = allowNegative ? '^-?' + reg0Str : '^' + reg0Str;
177  reg0Str = reg0Str + '$';
178  var reg0 = new RegExp(reg0Str);
179  if (reg0.test(temp)) return true;
180
181  // first replace all non numbers
182  var reg1Str = '[^0-9' + (decimalPlaces != 0 ? separator : '') + (allowNegative ? '-' : '') + ']';
183  var reg1 = new RegExp(reg1Str, 'g');
184  temp = temp.replace(reg1, '');
185
186  if (allowNegative) {
187    // replace extra negative
188    var hasNegative = temp.length > 0 && temp.charAt(0) == '-';
189    var reg2 = /-/g;
190    temp = temp.replace(reg2, '');
191    if (hasNegative) temp = '-' + temp;
192  }
193
194  if (decimalPlaces != 0) {
195    var reg3 = new RegExp('\\' + separator, 'g');
196    var reg3Array = reg3.exec(temp);
197    if (reg3Array != null) {
198      // keep only first occurrence of .
199      //  and the number of places specified by decimalPlaces or the entire string if decimalPlaces < 0
200      var reg3Right = temp.substring(reg3Array.index + reg3Array[0].length);
201      reg3Right = reg3Right.replace(reg3, '');
202      reg3Right = decimalPlaces > 0 ? reg3Right.substring(0, decimalPlaces) : reg3Right;
203      temp = temp.substring(0, reg3Array.index) + separator + reg3Right;
204    }
205  }
206  obj.value = temp;
207}
208
209var pricingAreaQ1 = 0;
210var pricingAreaQ2 = 0;
211var pricingAreaQ3 = 0;
212var pricingAreaQ4 = 0;
213var pricingAreaQ5 = 0;
214var pricingParameterOld = null;
215
216function getPricingParameters() {
217  var isKit = $('#kitArea').length > 0;
218  var kitAreaData = isKit ? alParseParams($('#kitArea :input').serialize()) : {};
219
220  var pricingParameters = $('#pricingArea').length > 0 ? alParseParams($('#pricingArea :input').serialize()) : isKit ? { Q1: kitAreaData.txtKitQuantity } : null;
221
222  if (pricingParameters && pricingParameters.SelectedUom && pricingParameters.SelectedUom != '' && $('#Q1').val() == '0')
223    $('#Q1').val(1);
224
225  if (pricingParameters)
226    pricingParameters.KitParameters = isKit ? kitAreaData.kitParameters : null;
227
228  return pricingParameters;
229}
230
231function onEngineLoaded(pricingParametersUsedToLoadEngine) {
232  // Bug 93397: the pricing call fired on load can fail by design (e.g. calculators whose
233  // default option is priced out of range to force a real selection). The error branch
234  // reverts Q1-Q5 to the last known-good values, which before any successful calculation
235  // were still the initial 0s - so the first failure wiped the server-rendered quantities
236  // and every later attempt (like just picking a valid option) kept failing with qty 0.
237  // Seed the known-good values from the freshly rendered inputs instead.
238  if (!pricingParameterOld) {
239    pricingAreaQ1 = $('#Q1').val() || pricingAreaQ1;
240    pricingAreaQ2 = $('#Q2').val() || pricingAreaQ2;
241    pricingAreaQ3 = $('#Q3').val() || pricingAreaQ3;
242    pricingAreaQ4 = $('#Q4').val() || pricingAreaQ4;
243    pricingAreaQ5 = $('#Q5').val() || pricingAreaQ5;
244  }
245
246  //This is reponsible for formatting the quantity fields on the pricing engine
247  $('.pricingGridQuantity').on('keyup',function (event) {
248    var decimalPlaces = $(this).data('decimalPlaces');
249    var decimalSeparator = $(this).data('decimalSeparator');
250    extractNumber($(this)[0], decimalPlaces, false, decimalSeparator);
251  });
252
253  $('.pricingGridQuantity, #pricingEngineArea input[type=text]').on('keypress',function (event) {
254    if (event.keyCode === 13) {
255      calculatePrice();
256      event.preventDefault();
257    }
258  });
259
260  var isKit = $('#kitArea').length > 0;
261  var calculatePrice = function () {
262    //disable add to cart and customize buttons to prevent pricing error on click before pricing calculated
263    // we add data-updating-price flag, so that we can avoid enabled the button when still uploading files 
264    $('body').attr('data-updating-price', 'true');
265    $("#btnAddToCartButton, #btnCustomizeButton").attr("disabled", "disabled");
266    
267    //Code to handle field suppressions
268    var inputsForSuppression = $('#pricingArea :input');
269
270    for (var input_index = 0; input_index < inputsForSuppression.length; input_index++) {
271      var element = inputsForSuppression[input_index];
272      var elementType = $(element).attr('type');
273      if (elementType && elementType.toLowerCase() == 'hidden')
274        continue;
275
276      var suppressionRule = $(element).data('suppression');
277      if (suppressionRule && suppressionRule.length > 0) {
278        var rules = suppressionRule.split(';');
279        for (var rule_index = 0; rule_index < rules.length; rule_index++) {
280          var isRuleMatched = false;
281          var rule = rules[rule_index];
282
283          var ruleParts = rule.split('=');
284          if (ruleParts.length > 1) {
285            var source = $('#pricingArea').find("[data-key='" + ruleParts[0] + "']").find(':input');
286
287            for (var src_index = 0; src_index < source.length; src_index++) {
288              var src_element = source[src_index];
289
290              var src_elementType = $(src_element).attr('type');
291              if (src_elementType && src_elementType.toLowerCase() == 'hidden')
292                continue;
293
294              var parentNode = $(element).parent('li');
295              if ($(src_element).val() == ruleParts[1]) {
296                var defaultValue = $(element).data('default');
297
298                if (defaultValue) {
299                  $(element).val(defaultValue);
300                }
301
302                parentNode.hide();
303                isRuleMatched = true;
304                break;
305              } else {
306                parentNode.show();
307              }
308            }
309          }
310          if (isRuleMatched)
311            break;
312        }
313      }
314    }
315
316    //Here we run reqiest start, this will eventually run other functions that custom skin developers hook into.
317    requestStart(null, null);
318    var pricingParameters = getPricingParameters();
319    //console.log('get pricing start');
320    $.ajax({
321      url: "/product/" + $("#urlName").val() + "/pricing",
322      type: "POST",
323      data: JSON.stringify({ pricingParameters: pricingParameters }),
324      dataType: "json",
325      contentType: "application/json; charset=utf-8;"
326    })
327    .done(function (data) {
328      //console.log('get pricing end', data);
329      if (isKit) {
330        if ($("#kitSetTotal").length > 0) //#kitSetTotal is rendered for KitOptionPricingType.EachSelectedKit only
331          $("#kitSetTotal").html(data["FormattedCost"]);
332        else if ($("#pricingEngine_lblFinalPrice").length > 0) //KitOptionPricingType.SingleProductEngine
333          $("#pricingEngine_lblFinalPrice").html(data["FormattedCost"]);
334
335        if (data["ErrorMessage"] && data["ErrorMessage"] != "") {
336          $("#KitSetError").html(data["ErrorMessage"]);
337          $("#KitSetError").show();
338        }
339        else {
340          $("#KitSetError").html();
341          $("#KitSetError").hide();
342        }
343      } else {
344        if (data["ErrorMessage"] && data["ErrorMessage"] != "") {
345          $("#priceErrorMessage").html(data["ErrorMessage"]);
346          $("#priceErrorMessage").removeClass('hidden');
347          $('#Q1').val(pricingAreaQ1);
348          $('#Q2').val(pricingAreaQ2);
349          $('#Q3').val(pricingAreaQ3);
350          $('#Q4').val(pricingAreaQ4);
351          $('#Q5').val(pricingAreaQ5);
352          //ResetCalculatorDropdownSelection(pricingParameterOld);
353          // Revert to previous value will cause issue, as use can not change two option at the same time.
354          // So not all combination is available to choose.
355          $("#pricingEngine_lblFinalPrice").html('#.##');
356          $("#pricingEngine_lblPerPiecePrice").html('#.##');
357          window.hasPricingEngineError = true;
358        }
359        else {
360          $("#priceErrorMessage").empty();
361          $("#priceErrorMessage").addClass('hidden');
362          pricingAreaQ1 = $('#Q1').val();
363          pricingAreaQ2 = $('#Q2').val();
364          pricingAreaQ3 = $('#Q3').val();
365          pricingAreaQ4 = $('#Q4').val();
366          pricingAreaQ5 = $('#Q5').val();
367          pricingParameterOld = pricingParameters;
368          $("#pricingEngine_lblFinalPrice").html(data["FormattedCost"]);
369          $("#pricingEngine_lblPerPiecePrice").html(data["FormattedPerUnitCost"]);
370          window.hasPricingEngineError = false;
371        }
372      }
373      $("#hdnTotalCost").val(data["Cost"]);
374      $("#hdnTotalWeight").val(data["Weight"]);
375
376      //Here we run response end, this will eventually run other functions that custom skin developers hook into.
377
378      // We enable the button if not uploading files
379      $('body').attr('data-updating-price', 'false');
380      if (parseInt($('body').attr("data-uploading-files") || "0", 10) <= 0) {
381          $("#btnAddToCartButton, #btnCustomizeButton").removeAttr("disabled");
382      }
383      
384      responseEnd(null, data);
385    })
386    .fail(function (error) {
387      console.error("Not able to retrieve price. Request failed.", error);
388      $("#priceErrorMessage").html("Not able to retrieve price. Request failed.").removeClass('hidden');
389      responseEnd(null, error);
390
391    });
392  }
393
394  var newPricingParameters = getPricingParameters();
395  var usedSamePricingParameters = pricingParametersUsedToLoadEngine && pricingParametersUsedToLoadEngine.length == newPricingParameters.length;
396  for (var i = 0; i < newPricingParameters.length; i++) {
397    usedSamePricingParameters = usedSamePricingParameters && pricingParametersUsedToLoadEngine && pricingParametersUsedToLoadEngine[i].Key == newPricingParameters[i].Key;
398  }
399
400  var supportSuppressions = $('#calcParmInputs').hasClass('supportSuppressions');
401  if (!supportSuppressions || usedSamePricingParameters || isKit) {
402    var changeCallback = supportSuppressions ? function (e) {
403      loadPricingEngine(getPricingParameters());
404
405      //get current active input
406      window.calcActiveInputID = e.currentTarget.id;
407      //console.log("set active element id: ", window.calcActiveInputID);
408    } : function () {
409      calculatePrice();
410    };
411
412    //if there are control other than textbox and select then need to add event for that
413    if ($("#pricingArea :input[type=number]").length) {
414      $("#pricingArea :input[type=number]").change(changeCallback);
415    }
416
417    if ($("#pricingArea :input[type=text]").length) {
418      $("#pricingArea :input[type=text]").on('change',changeCallback);
419    }
420    
421    //remove keypress events added before and make 'enter' key to trigger callback
422    $('.pricingGridQuantity, #pricingEngineArea input[type=text]').off("keypress").on("keypress", function (event) {
423      if (event.keyCode === 13) {
424        $(this).trigger('change');
425        event.preventDefault();
426      }
427    });
428      
429    if ($("#pricingArea select").length) {
430      $("#pricingArea select").on('change',changeCallback);
431    }
432
433    if ($("#kitArea :input").length) {
434      $("#kitArea :input").on('change',changeCallback);
435    }
436
437    $("#pricingArea :input[type=number], #pricingArea :input[type=text], #pricingArea select").on('focus', function (e) {
438      window.calcActiveInputID = e.currentTarget.id;
439    });
440
441    calculatePrice(); //fire on load
442  } else {
443    //console.log('used', pricingParametersUsedToLoadEngine ? pricingParametersUsedToLoadEngine.Options : null);
444    //console.log('new', newPricingParameters.Options);
445
446    loadPricingEngine(newPricingParameters);
447  }
448}
449
450function isInViewport(element) {
451    const rect = element.getBoundingClientRect();
452    return (
453        rect.top >= 0 &&
454        rect.left >= 0 &&
455        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
456        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
457    );
458}
459
460function loadPricingEngine(pricingParameters) {
461
462  kendo.ui.progress($('#pricingArea'), true);
463  $.ajax({
464    url: "/product/" + $("#urlName").val() + "/options/" + $("#orderItemId").val(),
465    type: "POST",
466    data: JSON.stringify({ pricingParameters: pricingParameters }),
467    dataType: "html",
468    contentType: "application/json; charset=utf-8;",
469  })
470  .done(function (result) {
471    $("#pricingEngineArea").html(result);
472    kendo.ui.progress($('#pricingArea'), false);
473    onEngineLoaded(pricingParameters);
474
475    if (!window.calcActiveInputID || window.calcActiveInputID == "undefined") {
476      if ($("#pricingArea :input").length) {
477        var $firstInput = $('#pricingArea :input').first();
478        if(isInViewport($firstInput.get(0))){
479          $firstInput.focus();
480        }
481      }
482      //console.log("set focus on first input");
483    } else {
484      document.getElementById(window.calcActiveInputID).focus();
485      //console.log("set focus on active element");
486    }
487
488    //console.log("loadPricingEngine done");
489  })
490  .fail(function (e) {
491    console.error("Product/Options AJAX ERROR: ", e);
492  });
493}
494
495$(document).ready(function () {
496  //console.log("document read");
497  loadPricingEngine(null);
498
499  $(window).on('unload',function () {
500    console.log("unload here");
501    window.kendo.ui.progress($('.addToCartButton'), false);
502    window.kendo.ui.progress($('#pricingArea'), false);
503  });
504});

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.