PageSourceSearch

https://puppycake.com/Shared/Themes/Puppycake/js/Spinner.js

js puppycake.com collected 2026-09-26 07:36:26 UTC 7,113 bytes, 213 lines download raw bytes

1/*
2*   Spinner.js
3***************************************************************
4*	  Adds Increment/Decrement buttons to the right of quantity text boxes.
5*
6* 	Copyright 2021 AmeriCommerce
7*/
8
9const getDefaults = () => ({
10  increment : 1,
11  delay : 250,
12  interval : 50,
13  allowNegative: false,
14  minQty : 0,
15  maxQty : false,
16  small: themeSettings.enableSmallSpinner,
17  defaultInputAria: "Product Quantity",
18  customClass: {},
19});
20
21function Spinner (element, options) {
22  const spinnerCode = () => {
23    if(themeSettings.enableSpinner) {
24      if(typeof element == "string") {
25        const elements = document.querySelectorAll(element);
26        elements.forEach(function(el){
27          new SpinnerEL(el, options);
28        });
29      }
30      else 
31        new SpinnerEL(element, options);
32    }
33  }
34  if (/complete|interactive|loaded/.test(document.readyState)) {
35    spinnerCode();
36  }
37  else
38    document.addEventListener("DOMContentLoaded", function() {
39      spinnerCode();
40    });
41}
42
43function SpinnerEL (element, options) {
44  var self = this;
45  this.element = typeof element == "string" ? document.querySelector(element) : element;
46  this.isProductPage = document.body.classList.contains('ProductDetails');
47  this.dataWrapper = this.element.closest(".qty-wrapper");
48  this.element.value = this.element.value || 0;
49  const defaults = getDefaults();
50
51  this.quantities = this.dataWrapper ? {
52    minQty : parseInt(self.dataWrapper.dataset["qtyMin"] || defaults.minQty, 10) || 0,
53    maxQty : parseInt(self.dataWrapper.dataset["qtyMax"] || defaults.maxQty, 10) || 0,
54    increment : parseInt(self.dataWrapper.dataset["qtyInc"] || defaults.increment) || 1,
55    allowNegative: self.dataWrapper.dataset["qtyNegative"] || defaults.allowNegative,
56    small: self.dataWrapper.dataset["qtySmall"] || defaults.small,
57    justify: self.dataWrapper.dataset["qtyJustify"],
58    inputAria: self.dataWrapper.dataset["qtyAria"] || defaults.defaultInputAria || ""
59  } : {minQty: 0, maxQty: 0, increment: 1, inputAria: "" };
60
61  this._defaults = {...defaults, ...this.quantities};
62
63  this.options = {...this._defaults, ...options};
64
65  if(typeof this.options.small == "string") 
66    this.options.small = this.options.small.toLowerCase() == "true";
67  if(typeof this.options.allowNegative == "string") 
68    this.options.allowNegative = this.options.allowNegative.toLowerCase() == "true";
69
70  this.updatePrice = undefined;
71  this.variantController = undefined;
72
73  if(window.variantController!=null && window.variantController.registry.length){
74    this.variantController = variantController.registry[0];
75    this.updatePrice = this.variantController.updatePrice.bind(this.variantController);
76  }
77  this.out = true;
78  this.wrapper = null;
79  this.up = null
80  this.down = null;
81
82  this.interval = null;
83  this.timeout = null;
84
85  this.val = parseInt(this.element.value);
86  this.prevVal = this.val;
87
88  this.init();
89}
90SpinnerEL.prototype = {
91  init: function(){    
92
93    const parentJustify = this.element.parentNode.getAttribute("qty-justify") || this.options.justify;
94    const { customClass, small, inputAria } = this.options;
95
96    // Apply classes to input
97    this.element.classList.add("border-0");
98    if(!isLabelled(this.element))
99      this.element.setAttribute("aria-label", inputAria)
100
101      // Wrap element node
102      const wrapper = document.createElement('div');
103    wrapper.classList.add("qty-spinner", "input-group", "d-inline-flex", "align-items-center");
104    if(parentJustify)
105      wrapper.classList.add(`justify-content-${parentJustify}`);
106    if(typeof customClass.wrapper == "string") wrapper.classList.add(...customClass.wrapper.split(/\s+/));
107    if(small) wrapper.classList.add("input-group-sm");
108    this.element.parentNode.insertBefore(wrapper, this.element);
109    wrapper.appendChild(this.element);
110    this.wrapper = this.element.parentElement;
111
112    // Create Buttons
113    this.up = document.createElement("div");
114    if(typeof customClass.up == "string") this.up.classList.add(...customClass.up.split(/\s+/));
115    this.up.innerHTML = `<a class='btn btn-outline-dark ${this.options.small?"btn-sm":""} spinnerBtn'><icon class='fas fa-plus'></icon></a>`;
116
117    this.down = document.createElement("div");
118    if(typeof customClass.down == "string") this.down.classList.add(...customClass.down.split(/\s+/));
119    this.down.innerHTML = `<a class='btn btn-outline-dark ${this.options.small?"btn-sm":""} spinnerBtn'><icon class='fas fa-minus'></icon></a>`;
120
121    this.wrapper.insertBefore(this.down, this.element)
122    this.wrapper.appendChild(this.up);
123
124    if(!this.variantController){
125      this.updatePrice = this.updatePriceFallback; 
126    }
127
128    this.bind();
129  },
130
131  updatePriceFallback: function(){
132    if (this.isProductPage) { 
133      var itemID = $("#hfItemID").val();
134      var quantity = $(this.element).val();
135      var params = {
136        itemID: itemID,
137        quantity: quantity,
138        variantIDs: [],
139        personalizationIds: [],
140        personalizationStrings: []
141      }; 
142      // "{'itemID': " + itemID + ", 'quantity': " + quantity + "}";
143      var self = this;
144      //console.log(params);
145      $.ajax({
146        type: "POST",
147        contentType: "application/json; charset=utf-8",
148        url: "/Store/Controls/ScriptService.asmx/GetPrice",
149        dataType: "json",
150        data: JSON.stringify(params),
151        success: function(response){
152          //console.log(response);
153          $(".PriceToUpdate").html(response.d.price);
154        }
155      });
156    }
157  },
158
159  bind: function(){
160    var self = this;
161
162    function down (dec) {
163      if(self.out) {
164        self.prevVal = this.val;
165        self.out = false;
166      }
167
168      if(dec)
169        self.decrement();
170      else
171        self.increment();
172
173
174      self.timeout = setTimeout(function(){
175        self.interval = setInterval($.proxy(dec?self.decrement:self.increment, self), self.options.interval);
176      }, self.options.delay);
177    }
178    function out () {
179      self.out = true;
180      self.updatePrice();
181      clearTimeout(self.timeout);
182      clearInterval(self.interval);
183      if(self.val !== self.prevVal)
184        self.element.dispatchEvent(new Event("change"));
185      self.prevVal = self.val;
186    }
187
188    this.up.addEventListener('mousedown', down.bind(self, false));
189    this.up.addEventListener('mouseup', out);
190    this.up.addEventListener('mouseleave', out);
191
192    this.down.addEventListener('mousedown', down.bind(self, true));
193    this.down.addEventListener('mouseup', out);
194    this.down.addEventListener('mouseleave', out);
195  },      
196
197  increment: function(){
198    var self = this;
199    this.val = parseInt(self.element.value, 10) + this.options.increment;
200    if (!this.options.maxQty || this.val <= this.options.maxQty)
201      this.element.value = this.val;
202    this.element.setAttribute("value", this.element.value);
203  },
204
205  decrement: function(){
206    var self = this;
207    this.val = parseInt(self.element.value, 10) - this.options.increment;
208    if (this.val >= this.options.minQty || this.options.allowNegative)
209      this.element.value = this.val;
210
211    this.element.setAttribute("value", this.element.value);
212  }      
213};

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.