PageSourceSearch

https://itgetsbetter.org/wp-content/plugins/printful-shipping-for-…/assets/js/product-size-guide.js?ver=7.1.2

js itgetsbetter.org collected 2026-09-24 08:37:12 UTC 18,411 bytes, 468 lines download raw bytes

1/** Define class */
2var Printful_Product_Size_Guide;
3
4(function () {
5    'use strict';
6
7    /**
8     * @type {{modal: null, onSizeGuideClick: onSizeGuideClick, closeModal: closeModal, createModal: (function(string): HTMLDivElement)}}
9     */
10    Printful_Product_Size_Guide = {
11        modal: null,
12        modalContentNode: null,
13        modalBodyNode: null,
14        sizeChartNode: null,
15        /**
16         * @var {{}}
17         * @var {Array<string>} sizeGuideData.availableSizes
18         * @var {{}} sizeGuideData.modelMeasurements
19         * @var {{}} sizeGuideData.productMeasurements
20         */
21        sizeGuideData: null,
22
23        /**
24         * Key is unit identifier (e.g. 'inch') value is translated unit name (e.g. 'Inches')
25         * @var {{}}
26         */
27        translatedUnitNames: null,
28
29        /**
30         * Handle click event
31         */
32        onSizeGuideClick: function () {
33            if (!window.pfGlobal || !window.pfGlobal.sg_data_raw) {
34                return;
35            }
36            this.sizeGuideData = JSON.parse(window.pfGlobal.sg_data_raw);
37
38            this.translatedUnitNames = {};
39            if (window.pfGlobal && window.pfGlobal.sg_unit_translations) {
40                this.translatedUnitNames = JSON.parse(window.pfGlobal.sg_unit_translations);
41            }
42            document.body.appendChild(this.createModal());
43        },
44
45        /**
46         * Close the modal element
47         */
48        closeModal: function () {
49            this.removeNode(this.sizeChartNode);
50            this.removeNode(this.modalContentNode);
51            this.removeNode(this.modalBodyNode);
52            this.removeNode(this.modal);
53            this.sizeChartNode = null;
54            this.modalContentNode = null;
55            this.modalBodyNode = null;
56            this.modal = null;
57        },
58
59        /**
60         * Create modal content
61         * @returns {HTMLDivElement}
62         */
63        createModal: function () {
64            // Clear the old one just to be sure
65            this.closeModal();
66            this.modal = this.buildEl('div', 'pf-size-guide-modal-wrapper');
67            this.modalBodyNode = this.buildEl('div', 'pf-size-guide-modal');
68            this.modalBodyNode.style.color = this.getTextColor();
69            this.modalBodyNode.style.backgroundColor = this.getBackGroundColor();
70            this.modalBodyNode.appendChild(this.buildModalHeader());
71
72            // Render initial active tab
73            this.renderModalContent(Printful_Product_Size_Guide.TAB_TYPE_PERSON);
74            this.modal.appendChild(this.modalBodyNode);
75
76            return this.modal;
77        },
78
79        buildEl: function (tagName, className, attributes) {
80            attributes = attributes || {};
81            var el = document.createElement(tagName);
82            if (className) {
83                el.className = className;
84            }
85
86            if (attributes.id) {
87                el.setAttribute('id', attributes.id);
88            }
89
90            if (attributes.innerHTML) {
91                el.innerHTML = attributes.innerHTML;
92            }
93
94            if (attributes.src) {
95                el.setAttribute('src', attributes.src);
96            }
97
98            if (attributes.onclick) {
99                el.onclick = attributes.onclick;
100            }
101
102            if (['td', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].indexOf(tagName) > -1) {
103                el.style.backgroundColor = this.getBackGroundColor();
104                el.style.color = this.getTextColor();
105            }
106
107            return el;
108        },
109
110        removeNode: function (node) {
111            if (node && node.parentNode && node.parentNode.tagName) {
112                node.parentNode.removeChild(node);
113            }
114        },
115
116        renderModalContent: function (activeTabType) {
117            this.removeNode(this.modalContentNode);
118            this.modalContentNode = this.buildEl('div', 'pf-size-guide-modal__content');
119
120            var tabData = this.getSizeGuideDataForTab(activeTabType),
121                tabTitle = this.getTabTitle(activeTabType);
122
123            this.modalContentNode.appendChild(this.buildSizeGuideTabsNode(activeTabType));
124            this.modalContentNode.appendChild(this.buildTabContent(tabData, tabTitle));
125
126            this.modalBodyNode.appendChild(this.modalContentNode);
127        },
128
129        getSizeGuideDataForTab: function (tabType) {
130            if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
131                return this.sizeGuideData.modelMeasurements;
132            }
133
134            if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PRODUCT) {
135                return this.sizeGuideData.productMeasurements;
136            }
137
138            return {};
139        },
140
141        getTabTitle: function (tabType) {
142            if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
143                return window.pfGlobal && window.pfGlobal.sg_tab_title_person ? window.pfGlobal.sg_tab_title_person : 'Measure yourself';
144            }
145
146            return window.pfGlobal && window.pfGlobal.sg_tab_title_product ? window.pfGlobal.sg_tab_title_product : 'Product measurements';
147        },
148
149        renderSizeChart: function (measurementData, selectedUnit) {
150            if (this.sizeChartNode) {
151                this.removeNode(this.sizeChartNode.firstChild);
152            } else {
153                this.sizeChartNode = this.buildEl('div', 'pf-size-guide-modal-size-chart');
154            }
155
156            this.sizeChartNode.appendChild(this.buildSizeChartBlock(measurementData, selectedUnit));
157        },
158
159        buildModalHeader: function () {
160            var title = window.pfGlobal && window.pfGlobal.sg_modal_title ? window.pfGlobal.sg_modal_title : 'Size guide',
161                wrapper = this.buildEl('div', 'pf-size-guide-modal__header');
162
163            wrapper.appendChild(this.buildEl('h4', 'pf-size-guide-modal__title', {innerHTML: title}));
164
165            var closeBtn = this.buildEl('button', 'pf-size-guide-modal__close'),
166                img = this.buildEl('img');
167
168            img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAAgVJREFUaAXtmU2KwkAQhZ0hQbyDu7mIB3PhwoN5EXfeIYguJo9JQRMydurnNYxTDTH+dNWr71VD2mSzyZEOpAPpQDqQDqQD6UA6kA6kA80dOJ1O++Px+NVKGFrQ9Oh9WoMhPAzD5fF4XFpAQwNa0PRAf1iABXaMle7e+r4/nM/nqyVfLUZgx3nS3etutzuMddxqsfPf1cALsJKTAr0AK3omaPWSfj6f21ERx3zso5f3C1hob6da5nW8/KzuMLJVCgnpNEvDBMyGZsGibjMwC5oJ6waOhmbDhgBHQbeADQP2QreCDQW2QreEDQfWQreGpQCvhcY8bFTGk2wX8ZWMkGu5JCvPrstSmWj+vta9aX5TWGjSgJG8Ao0p80HrrAhRgSGigKbDoh468EroJrCoRf1vCUF/edA7/K+WtAJWFg19adM6XIGVWzPvcVmqweL+F1r6FhuPNbBys08zV9a89xy6pC0AlhgPdBiwp3BPrBY+BDii4Igca+DdwJGFRub6Dd4FzCiQkbOENwMzC2PmNgEzC5JusDTUwKxCBLQ8M7TU/5a6rruPReGYj/B9MDYo065MtqKl5n2qpfyu+l7dYWRceIIYDltWvtBp05ND5DQBI7CA3jKfDUMLo4C+W58N/2RyvAIahThSqEKhBU1VUE5OB9KBdCAdSAfSgXQgHUgH0oEQB74BG1sUIwNoL3cAAAAASUVORK5CYII=";
169            closeBtn.appendChild(img);
170            closeBtn.onclick = (this.closeModal).bind(this);
171
172            wrapper.appendChild(closeBtn);
173            wrapper.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
174
175            return wrapper;
176        },
177
178        buildSizeGuideTabsNode: function (activeTabType) {
179            var tabs = this.buildEl('ul', 'pf-product-size-guide__tabs');
180            tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PERSON), Printful_Product_Size_Guide.TAB_TYPE_PERSON, activeTabType));
181            tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PRODUCT), Printful_Product_Size_Guide.TAB_TYPE_PRODUCT, activeTabType));
182
183            return tabs;
184        },
185
186        buildSizeGuideTabNode: function (title, type, activeType) {
187            var className = 'pf-product-size-guide__tab';
188
189            if (type === activeType) {
190                className += ' pf-product-size-guide__tab--active';
191            }
192
193            var tab = this.buildEl('li', className, {
194                innerHTML: title,
195                onclick: (this.renderModalContent).bind(this, type)
196            });
197
198            if (type === activeType) {
199                tab.style.backgroundColor = this.getTabBackGroundColor(true);
200            } else {
201                tab.style.backgroundColor = this.getTabBackGroundColor(false);
202            }
203
204            return tab;
205        },
206
207        buildTabContent: function (measurementData, title) {
208            measurementData = measurementData || {};
209            var node = this.buildEl('div');
210            node.appendChild(this.buildEl('h4', null, {innerHTML: title}));
211            if (measurementData.hasOwnProperty('description')) {
212                node.appendChild(this.buildEl('div', null, {innerHTML: measurementData.description}));
213            }
214
215            // Model description
216            node.appendChild(this.buildDescriptionBlock(measurementData));
217
218            // Size table
219            this.renderSizeChart(measurementData);
220            node.appendChild(this.sizeChartNode);
221
222            return node;
223        },
224
225        buildDescriptionBlock: function (measurementData) {
226            var node = this.buildEl('div', 'pf-size-guide-modal-measurements');
227            if (measurementData.hasOwnProperty('imageUrl') && measurementData.imageUrl) {
228                var measurementImgNode = this.buildEl('div', 'pf-size-guide-modal-measurements__image');
229                measurementImgNode.appendChild(this.buildEl('img', null, {src: measurementData.imageUrl}));
230
231                if (measurementData.hasOwnProperty('modelDescription')) {
232                    measurementImgNode.appendChild(this.buildEl('div', null, {innerHTML: measurementData.modelDescription}));
233                }
234                node.appendChild(measurementImgNode);
235            }
236
237            if (measurementData.hasOwnProperty('imageDescription')) {
238                node.appendChild(this.buildEl('div', 'pf-size-guide-modal-measurements__description', {innerHTML: measurementData.imageDescription}));
239            }
240            node.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
241
242            return node;
243        },
244
245        /**
246         * @param {{}} measurementData
247         * @param {string} [selectedUnit]
248         * @return {*}
249         */
250        buildSizeChartBlock: function (measurementData, selectedUnit) {
251            var node = this.buildEl('div');
252
253            if (!measurementData.hasOwnProperty('sizeTableRows') || measurementData.sizeTableRows.length < 1) {
254                return node;
255            }
256            var sizeRows = measurementData.sizeTableRows,
257                availableUnits = this.getUniqueUnits(sizeRows);
258
259            // Selected or first if nothing selected
260            selectedUnit = selectedUnit || this.getDefaultUnit(availableUnits);
261            node.appendChild(this.buildSizeChartTabsNode(measurementData, availableUnits, selectedUnit));
262            var wrapper = this.buildEl('div');
263            wrapper.style.overflowX = 'auto';
264            wrapper.appendChild(this.buildSizeChartTable(this.getSortedChartRows(sizeRows, selectedUnit)));
265            node.appendChild(wrapper);
266
267            return node;
268        },
269
270        /**
271         * @param {{}} measurementData
272         * @param {Array<{key, title}>} availableUnits
273         * @param {string} selectedUnit
274         * @return {*}
275         */
276        buildSizeChartTabsNode: function (measurementData, availableUnits, selectedUnit) {
277            availableUnits = availableUnits || [];
278            var tabsNode = this.buildEl('ul', 'pf-size-guide-modal-size-chart__tabs');
279
280            availableUnits.map((function (item) {
281                var className = 'pf-size-guide-modal-size-chart__tab';
282                if (item.key === selectedUnit) {
283                    className += ' pf-size-guide-modal-size-chart__tab--active';
284                }
285                tabsNode.appendChild(this.buildEl('li', className, {
286                    innerHTML: item.title,
287                    onclick: (this.renderSizeChart).bind(this, measurementData, item.key)
288                }));
289            }).bind(this));
290
291            return tabsNode;
292        },
293
294        buildSizeChartTable: function (rows) {
295            var tableNode = this.buildEl('table', 'pf-size-guide-modal-size-chart__table'),
296                availableSizes = this.sizeGuideData.availableSizes;
297
298            var tableHeader = this.buildEl('thead'),
299                tableHeaderRow = this.buildEl('tr');
300
301            var tableHeaderRowText = window.pfGlobal && window.pfGlobal.sg_table_header_size ? window.pfGlobal.sg_table_header_size : 'Size';
302
303            tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: tableHeaderRowText}));
304            rows.map((function (row) {
305                tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: row.title}));
306            }).bind(this));
307
308            tableHeader.appendChild(tableHeaderRow);
309            tableNode.appendChild(tableHeader);
310
311            // Loop all available sizes and print out a row for each type
312            var tableBody = this.buildEl('tbody');
313            availableSizes.map((function (size) {
314                var tableBodyRow = this.buildEl('tr');
315                tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: size}));
316                rows.map((function (row) {
317                    tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: row.sizes[size] || ''}));
318                }).bind(this));
319
320                tableBody.appendChild(tableBodyRow);
321            }).bind(this));
322
323            tableNode.appendChild(tableBody);
324
325            return tableNode;
326        },
327
328        getSortedChartRows: function (sizeTableRows, selectedUnit) {
329            return sizeTableRows
330                .filter(function (row) {
331                    return row.unit === selectedUnit;
332                }).map((function (row) {
333                    var sizes = {};
334                    for (var k in row.sizes) {
335                        if (!row.sizes.hasOwnProperty(k)) {
336                            continue;
337                        }
338
339                        // Convert to fractions if needed, round and join
340                        sizes[k] = row.sizes[k].map((function (size) {
341                            if (row.unit === Printful_Product_Size_Guide.UNIT_INCH) {
342                                return this.convertToFraction(size);
343                            } else {
344                                return size.toFixed(1);
345                            }
346                        }).bind(this)).join(' - ');
347                    }
348
349                    return {
350                        title: row.title,
351                        sizes: sizes
352                    };
353                }).bind(this));
354        },
355
356        convertToFraction: function (value) {
357            var split = String(value).split('.'),
358                integer = parseInt(split[0], 10);
359
360            if (!split[1]) {
361                return value;
362            }
363
364            var decimal = parseFloat('0.' + split[1]),
365                fraction = 0;
366
367            for (var fra in Printful_Product_Size_Guide.FRACTION_MAP) {
368                var compareValue = Printful_Product_Size_Guide.FRACTION_MAP[fra];
369
370                if (decimal < compareValue) {
371                    fraction = fra;
372                    break;
373                }
374            }
375
376            if (fraction === 0 && integer) {
377                integer++;
378                fraction = '';
379            }
380
381            return (integer > 0 ? integer + ' ' : '') + fraction;
382        },
383
384        /**
385         * @param {Array<{unit, unitName}>}sizeTableRows
386         * @return {Array<{key, title}>}
387         */
388        getUniqueUnits: function (sizeTableRows) {
389            var uniqueUnits = {};
390            sizeTableRows.map(function (row) {
391                // Gather unique units
392                if (!uniqueUnits.hasOwnProperty(row.unit)) {
393                    uniqueUnits[row.unit] = row.unitName;
394                }
395            });
396
397            var units = [];
398            for (var i in uniqueUnits) {
399                if (uniqueUnits.hasOwnProperty(i)) {
400                    units.push({
401                        key: i,
402                        title: this.getTranslatedUnitName(i, uniqueUnits[i])
403                    });
404                }
405            }
406
407            return units;
408        },
409
410        /**
411         * @param {string} unit
412         * @param {string} defaultValue
413         * @return {null|string}
414         */
415        getTranslatedUnitName: function(unit, defaultValue) {
416            if (this.translatedUnitNames[unit]) {
417                return this.translatedUnitNames[unit];
418            }
419
420            return defaultValue;
421        },
422
423        getBackGroundColor: function () {
424            return window.pfGlobal && window.pfGlobal.sg_modal_background_color ? window.pfGlobal.sg_modal_background_color : '#FFF';
425        },
426
427        getTextColor: function () {
428            return window.pfGlobal && window.pfGlobal.sg_modal_text_color ? window.pfGlobal.sg_modal_text_color : '#000';
429        },
430
431        getTabBackGroundColor: function (isActive) {
432            if (isActive) {
433                return window.pfGlobal && window.pfGlobal.sg_active_tab_background_color ? window.pfGlobal.sg_active_tab_background_color : '#FFF';
434            }
435
436            return window.pfGlobal && window.pfGlobal.sg_tab_background_color ? window.pfGlobal.sg_tab_background_color : '#EEE';
437        },
438
439        getDefaultUnit: function (availableUnits) {
440            availableUnits = availableUnits || [];
441            var uniqueUnitTypes = availableUnits.map(function (item) {
442                return item.key;
443            });
444
445            var defaultUnit = uniqueUnitTypes[0];
446            if (window.pfGlobal && window.pfGlobal.sg_primary_unit && uniqueUnitTypes.indexOf(window.pfGlobal.sg_primary_unit) > -1) {
447                return window.pfGlobal.sg_primary_unit;
448            }
449
450            return defaultUnit;
451        }
452    };
453
454    Printful_Product_Size_Guide.TAB_TYPE_PERSON = 'person';
455    Printful_Product_Size_Guide.TAB_TYPE_PRODUCT = 'product';
456    Printful_Product_Size_Guide.UNIT_INCH = 'inch';
457
458    Printful_Product_Size_Guide.FRACTION_MAP = {
459        ' ': 0.0625, //don't add any fraction
460        '⅛': 0.187,
461        '¼': 0.3125,
462        '⅜': 0.4375,
463        '½': 0.5625,
464        '⅝': 0.6875,
465        '¾': 0.8125,
466        '⅞': 0.9375
467    };
468})();

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.