PageSourceSearch

https://www.perfectdraft.com/static/version0.0.0.1228/frontend/Bee…/js/internals/common.js

js perfectdraft.com collected 2026-10-09 22:39:07 UTC 29,014 bytes, 606 lines download raw bytes

1var algolia = {
2    allowedHooks: [
3        'beforeAutocompleteSources',
4        'beforeAutocompleteOptions',
5        'afterAutocompleteStart',
6        'beforeInstantsearchInit',
7        'beforeWidgetInitialization',
8        'beforeInstantsearchStart',
9        'afterInstantsearchStart',
10        'afterInsightsBindEvents',
11        'beforeAutocompleteProductSourceOptions'
12    ],
13    registeredHooks: [],
14    registerHook: function (hookName, callback) {
15        if (this.allowedHooks.indexOf(hookName) === -1) {
16            throw 'Hook "' + hookName + '" cannot be defined. Please use one of ' + this.allowedHooks.join(', ');
17        }
18
19        if (!this.registeredHooks[hookName]) {
20            this.registeredHooks[hookName] = [callback];
21        } else {
22            this.registeredHooks[hookName].push(callback);
23        }
24    },
25    getRegisteredHooks: function(hookName) {
26        if (this.allowedHooks.indexOf(hookName) === -1) {
27            throw 'Hook "' + hookName + '" cannot be defined. Please use one of ' + this.allowedHooks.join(', ');
28        }
29
30        if (!this.registeredHooks[hookName]) {
31            return [];
32        }
33
34        return this.registeredHooks[hookName];
35    },
36    triggerHooks: function () {
37        var hookName = arguments[0],
38            originalData = arguments[1],
39            hookArguments = Array.prototype.slice.call(arguments, 2);
40
41        var data = this.getRegisteredHooks(hookName).reduce(function(currentData, hook) {
42            if (Array.isArray(currentData)) {
43                currentData = [currentData];
44            }
45            var allParameters = [].concat(currentData).concat(hookArguments);
46            return hook.apply(null, allParameters);
47        }, originalData);
48
49        return data;
50    }
51};
52
53requirejs(['algoliaBundle'], function(algoliaBundle) {
54    algoliaBundle.$(function ($) {
55        window.isMobile = function() {
56            var check = false;
57
58            (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
59
60            return check;
61        };
62
63        window.getCookie = function(name) {
64            var value = "; " + document.cookie;
65            var parts = value.split("; " + name + "=");
66            if (parts.length == 2) {
67                return parts.pop().split(";").shift();
68            }
69
70            return "";
71        };
72
73        window.transformHit = function (hit, price_key, helper) {
74            if (Array.isArray(hit.categories))
75                hit.categories = hit.categories.join(', ');
76
77            if (hit._highlightResult.categories_without_path && Array.isArray(hit.categories_without_path)) {
78                hit.categories_without_path = $.map(hit._highlightResult.categories_without_path, function (category) {
79                    return category.value;
80                });
81
82                hit.categories_without_path = hit.categories_without_path.join(', ');
83            }
84
85            var matchedColors = [];
86
87            if (helper && algoliaConfig.useAdaptiveImage === true) {
88                if (hit.images_data && helper.state.facetsRefinements.color) {
89                    matchedColors = helper.state.facetsRefinements.color.slice(0); // slice to clone
90                }
91
92                if (hit.images_data && helper.state.disjunctiveFacetsRefinements.color) {
93                    matchedColors = helper.state.disjunctiveFacetsRefinements.color.slice(0); // slice to clone
94                }
95            }
96
97            if (Array.isArray(hit.color)) {
98                var colors = [];
99
100                $.each(hit._highlightResult.color, function (i, color) {
101                    if (color.matchLevel === undefined || color.matchLevel === 'none') {
102                        return;
103                    }
104
105                    colors.push(color);
106
107                    if (algoliaConfig.useAdaptiveImage === true) {
108                        var matchedColor = color.matchedWords.join(' ');
109                        if (hit.images_data && color.fullyHighlighted && color.fullyHighlighted === true) {
110                            matchedColors.push(matchedColor);
111                        }
112                    }
113                });
114
115                hit._highlightResult.color = colors;
116            }
117            else {
118                if (hit._highlightResult.color && hit._highlightResult.color.matchLevel === 'none') {
119                    hit._highlightResult.color = { value: '' };
120                }
121            }
122
123            if (algoliaConfig.useAdaptiveImage === true) {
124                $.each(matchedColors, function (i, color) {
125                    color = color.toLowerCase();
126
127                    if (hit.images_data[color]) {
128                        hit.image_url = hit.images_data[color];
129                        hit.thumbnail_url = hit.images_data[color];
130
131                        return false;
132                    }
133                });
134            }
135
136            if (hit._highlightResult.color && hit._highlightResult.color.value && hit.categories_without_path) {
137                if (hit.categories_without_path.indexOf('<em>') === -1 && hit._highlightResult.color.value.indexOf('<em>') !== -1) {
138                    hit.categories_without_path = '';
139                }
140            }
141
142            if (Array.isArray(hit._highlightResult.name))
143                hit._highlightResult.name = hit._highlightResult.name[0];
144
145            if (Array.isArray(hit.price)) {
146                hit.price = hit.price[0];
147                if (hit['price'] !== undefined && price_key !== '.' + algoliaConfig.currencyCode + '.default' && hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_formated'] !== hit['price'][algoliaConfig.currencyCode]['default_formated']) {
148                    hit['price'][algoliaConfig.currencyCode][price_key.substr(1) + '_original_formated'] = hit['price'][algoliaConfig.currencyCode]['default_formated'];
149                }
150
151                if (hit['price'][algoliaConfig.currencyCode]['default_original_formated']
152                    && hit['price'][algoliaConfig.currencyCode]['special_to_date']) {
153                    var priceExpiration = hit['price'][algoliaConfig.currencyCode]['special_to_date'];
154
155                    if (algoliaConfig.now > priceExpiration + 1) {
156                        hit['price'][algoliaConfig.currencyCode]['default_formated'] = hit['price'][algoliaConfig.currencyCode]['default_original_formated'];
157                        hit['price'][algoliaConfig.currencyCode]['default_original_formated'] = false;
158                    }
159                }
160            }
161
162            // Add to cart parameters
163            var action = algoliaConfig.instant.addToCartParams.action + 'product/' + hit.objectID + '/';
164
165            // This method often returns an empty line - the method does not work properly
166            var correctFKey = getCookie('form_key');
167
168            if(correctFKey != "" && algoliaConfig.instant.addToCartParams.formKey != correctFKey) {
169                algoliaConfig.instant.addToCartParams.formKey = correctFKey;
170            }
171
172            hit.addToCart = {
173                'action': action,
174                'uenc': AlgoliaBase64.mageEncode(action),
175                'formKey': algoliaConfig.instant.addToCartParams.formKey
176            };
177
178            if (hit.__queryID) {
179
180                hit.urlForInsights = hit.url;
181
182                if (algoliaConfig.ccAnalytics.enabled
183                    && algoliaConfig.ccAnalytics.conversionAnalyticsMode !== 'disabled') {
184                    var insightsDataUrlString = $.param({
185                        queryID: hit.__queryID,
186                        objectID: hit.objectID,
187                        indexName: hit.__indexName
188                    });
189                    if (hit.url.indexOf('?') > -1) {
190                        hit.urlForInsights += insightsDataUrlString
191                    } else {
192                        hit.urlForInsights += '?' + insightsDataUrlString;
193                    }
194                }
195            }
196
197            return hit;
198        };
199
200        window.fixAutocompleteCssHeight = function () {
201            if ($(document).width() > 768) {
202                $(".other-sections").css('min-height', '0');
203                $(".aa-dataset-products").css('min-height', '0');
204                var height = Math.max($(".other-sections").outerHeight(), $(".aa-dataset-products").outerHeight());
205                $(".aa-dataset-products").css('min-height', height);
206            }
207        };
208
209        window.fixAutocompleteCssSticky = function (menu) {
210            var dropdown_menu = $('#algolia-autocomplete-container .aa-dropdown-menu');
211            var autocomplete_container = $('#algolia-autocomplete-container');
212            autocomplete_container.removeClass('reverse');
213
214            /** Reset computation **/
215            dropdown_menu.css('top', '0px');
216
217            /** Stick menu vertically to the input **/
218            var targetOffset = Math.round(menu.offset().top + menu.outerHeight());
219            var currentOffset = Math.round(autocomplete_container.offset().top);
220
221            dropdown_menu.css('top', (targetOffset - currentOffset) + 'px');
222
223            if (menu.offset().left + menu.outerWidth() / 2 > $(document).width() / 2) {
224                /** Stick menu horizontally align on right to the input **/
225                dropdown_menu.css('right', '0px');
226                dropdown_menu.css('left', 'auto');
227
228                var targetOffset = Math.round(menu.offset().left + menu.outerWidth());
229                var currentOffset = Math.round(autocomplete_container.offset().left + autocomplete_container.outerWidth());
230
231                dropdown_menu.css('right', (currentOffset - targetOffset) + 'px');
232            }
233            else {
234                /** Stick menu horizontally align on left to the input **/
235                dropdown_menu.css('left', 'auto');
236                dropdown_menu.css('right', '0px');
237                autocomplete_container.addClass('reverse');
238
239                var targetOffset = Math.round(menu.offset().left);
240                var currentOffset = Math.round(autocomplete_container.offset().left);
241
242                dropdown_menu.css('left', (targetOffset - currentOffset) + 'px');
243            }
244        };
245
246        if (typeof algoliaConfig !== 'undefined') {
247            $(algoliaConfig.autocomplete.selector).each(function () {
248                $(this).closest('form').submit(function (e) {
249                    var query = $(this).find(algoliaConfig.autocomplete.selector).val();
250
251                    query = encodeURIComponent(query);
252
253                    if (algoliaConfig.instant.enabled && query === '')
254                        query = '__empty__';
255
256                    window.location = $(this).attr('action') + '?q=' + query;
257
258                    return false;
259                });
260            });
261        }
262
263        function handleInputCrossAutocomplete(input) {
264            if (input.val().length > 0) {
265                input.closest('#algolia-searchbox').find('.clear-query-autocomplete').show();
266                input.closest('#algolia-searchbox').find('.magnifying-glass').hide();
267            }
268            else {
269                input.closest('#algolia-searchbox').find('.clear-query-autocomplete').hide();
270                input.closest('#algolia-searchbox').find('.magnifying-glass').show();
271            }
272        }
273
274        window.createISWidgetContainer = function (attributeName) {
275            var div = document.createElement('div');
276            div.className = 'is-widget-container-' + attributeName.split('.').join('_');
277            div.dataset.attr = attributeName;
278
279            return div;
280        };
281
282        $(document).on('click', '.clear-query-autocomplete', function () {
283            var input = $(this).closest('#algolia-searchbox').find('input');
284
285            input.val('');
286            input.get(0).dispatchEvent(new Event('input'));
287
288            handleInputCrossAutocomplete(input);
289        });
290
291        /** Handle small screen **/
292        $('body').on('click', '#refine-toggle', function () {
293            $('#instant-search-facets-container').toggleClass('hidden-sm').toggleClass('hidden-xs');
294            if ($(this).html().trim()[0] === '+')
295                $(this).html('- ' + algoliaConfig.translations.refine);
296            else
297                $(this).html('+ ' + algoliaConfig.translations.refine);
298        });
299
300		$('body').on('click', '#show-filters-button', function () {
301			$('.filters-wrapper').removeClass('hide-filters');
302		});
303
304		$('body').on('click', '.hide-filters-button', function () {
305			$('.filters-wrapper').addClass('hide-filters');
306		});
307
308        // The url is now rendered as follows : http://website.com?q=searchquery&facet1=value&facet2=value1~value2
309        // "?" and "&" are used to be fetched easily inside Magento for the backend rendering
310        // Multivalued facets use "~" as separator
311        // Targeted index is defined by sortBy parameter
312        window.routing = {
313            router: algoliaBundle.instantsearch.routers.history({
314                parseURL: function (qsObject) {
315                    var location = qsObject.location,
316                        qsModule = qsObject.qsModule;
317                    const queryString = location.hash ? location.hash : location.search;
318                    return qsModule.parse(queryString.slice(1))
319                },
320                createURL: function (qsObject) {
321                    var qsModule = qsObject.qsModule,
322                        routeState = qsObject.routeState,
323                        location = qsObject.location;
324                    const protocol = location.protocol,
325                        hostname = location.hostname,
326                        port = location.port ? location.port : '',
327                        pathname = location.pathname,
328                        hash = location.hash;
329
330                    const queryString = qsModule.stringify(routeState);
331                    const portWithPrefix = port === '' ? '' : ':' + port;
332                    // IE <= 11 has no location.origin or buggy. Therefore we don't rely on it
333                    if (!routeState || Object.keys(routeState).length === 0) {
334                        return protocol + '//' + hostname + portWithPrefix + pathname;
335                    }
336                    else {
337                        if (queryString && queryString != 'q=__empty__') {
338                            return protocol + '//' + hostname + portWithPrefix + pathname + '?' + queryString;
339                        } else {
340                            return protocol + '//' + hostname + portWithPrefix + pathname;
341                        }
342                    }
343                },
344            }),
345            stateMapping: {
346                stateToRoute: function (uiState) {
347                    var productIndexName = algoliaConfig.indexName + '_products';
348                    var uiStateProductIndex = uiState[productIndexName] || {};
349                    var routeParameters = {};
350                    if (algoliaConfig.isCategoryPage) {
351                        routeParameters['q'] = uiState[productIndexName].query;
352                    } else if (algoliaConfig.isLandingPage){
353                        routeParameters['q'] = uiState[productIndexName].query || algoliaConfig.landingPage.query || '__empty__';
354                    } else{
355                        routeParameters['q'] = uiState[productIndexName].query || algoliaConfig.request.query || '__empty__';
356                    }
357                    if (algoliaConfig.facets) {
358                        for(var i=0; i<algoliaConfig.facets.length; i++) {
359                            var currentFacet = algoliaConfig.facets[i];
360                            // Handle refinement facets
361                            if (currentFacet.attribute != 'categories' && (currentFacet.type == 'conjunctive' || currentFacet.type == 'disjunctive')) {
362                                routeParameters[currentFacet.attribute] = (uiStateProductIndex.refinementList &&
363                                    uiStateProductIndex.refinementList[currentFacet.attribute] &&
364                                    uiStateProductIndex.refinementList[currentFacet.attribute].join('~'));
365                            }
366                            // Handle categories
367                            if (currentFacet.attribute == 'categories' && !algoliaConfig.isCategoryPage) {
368                                routeParameters[currentFacet.attribute] = (uiStateProductIndex.hierarchicalMenu &&
369                                    uiStateProductIndex.hierarchicalMenu[currentFacet.attribute+ '.level0'] &&
370                                    uiStateProductIndex.hierarchicalMenu[currentFacet.attribute+ '.level0'].join('~'));
371                            }
372                            // Handle sliders
373                            if (currentFacet.type == 'slider' || currentFacet.type == 'priceRanges') {
374                                routeParameters[currentFacet.attribute] = (uiStateProductIndex.range &&
375                                    uiStateProductIndex.range[currentFacet.attribute] &&
376                                    uiStateProductIndex.range[currentFacet.attribute]);
377                            }
378                        };
379                    }
380                    routeParameters['sortBy'] = uiStateProductIndex.sortBy;
381                    routeParameters['page'] = uiStateProductIndex.page;
382                    return routeParameters;
383                },
384                routeToState: function (routeParameters) {
385                    var productIndexName = algoliaConfig.indexName + '_products';
386                    var uiStateProductIndex = {}
387
388                    uiStateProductIndex['query'] = routeParameters.q == '__empty__' ? '' : routeParameters.q;
389                    if (algoliaConfig.isLandingPage && typeof uiStateProductIndex['query'] === 'undefined' && algoliaConfig.landingPage.query != '') {
390                        uiStateProductIndex['query'] = algoliaConfig.landingPage.query;
391                    }
392
393                    var landingPageConfig = algoliaConfig.isLandingPage && algoliaConfig.landingPage.configuration ?
394                        JSON.parse(algoliaConfig.landingPage.configuration) :
395                        {};
396
397                    uiStateProductIndex['refinementList'] = {};
398                    uiStateProductIndex['hierarchicalMenu'] = {};
399                    uiStateProductIndex['range'] = {};
400                    if (algoliaConfig.facets) {
401                        for(var i=0; i<algoliaConfig.facets.length; i++) {
402                            var currentFacet = algoliaConfig.facets[i];
403                            // Handle refinement facets
404                            if (currentFacet.attribute != 'categories' && (currentFacet.type == 'conjunctive' || currentFacet.type == 'disjunctive')) {
405                                uiStateProductIndex['refinementList'][currentFacet.attribute] = routeParameters[currentFacet.attribute] && routeParameters[currentFacet.attribute].split('~');
406                                if (algoliaConfig.isLandingPage &&
407                                    typeof uiStateProductIndex['refinementList'][currentFacet.attribute] === 'undefined' &&
408                                    currentFacet.attribute in landingPageConfig) {
409                                    uiStateProductIndex['refinementList'][currentFacet.attribute] = landingPageConfig[currentFacet.attribute].split('~');
410                                }
411                            }
412                            // Handle categories facet
413                            if (currentFacet.attribute == 'categories' && !algoliaConfig.isCategoryPage) {
414                                uiStateProductIndex['hierarchicalMenu']['categories.level0'] = routeParameters['categories'] && routeParameters['categories'].split('~');
415                                if (algoliaConfig.isLandingPage &&
416                                    typeof uiStateProductIndex['hierarchicalMenu']['categories.level0'] === 'undefined' &&
417                                    'categories.level0' in landingPageConfig) {
418                                    uiStateProductIndex['hierarchicalMenu']['categories.level0'] = landingPageConfig['categories.level0'].split(' /// ');
419                                }
420                            }
421                            if (currentFacet.attribute == 'categories' && algoliaConfig.isCategoryPage) {
422                                uiStateProductIndex['hierarchicalMenu']['categories.level0'] = [algoliaConfig.request.path];
423                            }
424                            // Handle sliders
425                            if (currentFacet.type == 'slider' || currentFacet.type == 'priceRanges') {
426                                var currentFacetAttribute = currentFacet.attribute;
427                                uiStateProductIndex['range'][currentFacetAttribute] = routeParameters[currentFacetAttribute] && routeParameters[currentFacetAttribute];
428                                if (algoliaConfig.isLandingPage &&
429                                    typeof uiStateProductIndex['range'][currentFacetAttribute] === 'undefined' &&
430                                    currentFacetAttribute in landingPageConfig) {
431
432                                    var facetValue = '';
433                                    if (typeof landingPageConfig[currentFacetAttribute]['>='] !== "undefined") {
434                                        facetValue = landingPageConfig[currentFacetAttribute]['>='][0];
435                                    }
436                                    facetValue += ':';
437                                    if (typeof landingPageConfig[currentFacetAttribute]['<='] !== "undefined") {
438                                        facetValue += landingPageConfig[currentFacetAttribute]['<='][0];
439                                    }
440                                    uiStateProductIndex['range'][currentFacetAttribute] = facetValue;
441                                }
442                            }
443                        };
444                    }
445                    uiStateProductIndex['sortBy'] = routeParameters.sortBy;
446                    uiStateProductIndex['page'] = routeParameters.page;
447
448                    var uiState = {};
449                    uiState[productIndexName] = uiStateProductIndex;
450                    return uiState;
451                }
452            }
453        };
454    });
455});
456
457// Taken from Magento's tools.js - not included on frontend, only in backend
458var AlgoliaBase64 = {
459    // private property
460    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
460+/=",
461    //'+/=', '-_,'
462    // public method for encoding
463    encode: function (input) {
464        var output = "";
465        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
466        var i = 0;
467
468        if( typeof window.btoa === "function" ){
469            return window.btoa(input);
470        }
471
472        input = AlgoliaBase64._utf8_encode(input);
473
474        while (i < input.length) {
475
476            chr1 = input.charCodeAt(i++);
477            chr2 = input.charCodeAt(i++);
478            chr3 = input.charCodeAt(i++);
479
480            enc1 = chr1 >> 2;
481            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
482            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
483            enc4 = chr3 & 63;
484
485            if (isNaN(chr2)) {
486                enc3 = enc4 = 64;
487            } else if (isNaN(chr3)) {
488                enc4 = 64;
489            }
490            output = output +
491                this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
492                this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
493        }
494
495        return output;
496    },
497
498    // public method for decoding
499    decode: function (input) {
500        var output = "";
501        var chr1, chr2, chr3;
502        var enc1, enc2, enc3, enc4;
503        var i = 0;
504
505        if( typeof window.atob === "function" ){
506            return window.atob(input);
507        }
508
509        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
510
511        while (i < input.length) {
512
513            enc1 = this._keyStr.indexOf(input.charAt(i++));
514            enc2 = this._keyStr.indexOf(input.charAt(i++));
515            enc3 = this._keyStr.indexOf(input.charAt(i++));
516            enc4 = this._keyStr.indexOf(input.charAt(i++));
517
518            chr1 = (enc1 << 2) | (enc2 >> 4);
519            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
520            chr3 = ((enc3 & 3) << 6) | enc4;
521
522            output = output + String.fromCharCode(chr1);
523
524            if (enc3 !== 64) {
525                output = output + String.fromCharCode(chr2);
526            }
527            if (enc4 !== 64) {
528                output = output + String.fromCharCode(chr3);
529            }
530        }
531        output = AlgoliaBase64._utf8_decode(output);
532        return output;
533    },
534
535    mageEncode: function(input){
536        return this.encode(input).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ',');
537    },
538
539    mageDecode: function(output){
540        output = output.replace(/\-/g, '+').replace(/_/g, '/').replace(/,/g, '=');
541        return this.decode(output);
542    },
543
544    idEncode: function(input){
545        return this.encode(input).replace(/\+/g, ':').replace(/\//g, '_').replace(/=/g, '-');
546    },
547
548    idDecode: function(output){
549        output = output.replace(/\-/g, '=').replace(/_/g, '/').replace(/\:/g, '\+');
550        return this.decode(output);
551    },
552
553    // private method for UTF-8 encoding
554    _utf8_encode : function (string) {
555        string = string.replace(/\r\n/g,"\n");
556        var utftext = "";
557
558        for (var n = 0; n < string.length; n++) {
559
560            var c = string.charCodeAt(n);
561
562            if (c < 128) {
563                utftext += String.fromCharCode(c);
564            }
565            else if((c > 127) && (c < 2048)) {
566                utftext += String.fromCharCode((c >> 6) | 192);
567                utftext += String.fromCharCode((c & 63) | 128);
568            }
569            else {
570                utftext += String.fromCharCode((c >> 12) | 224);
571                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
572                utftext += String.fromCharCode((c & 63) | 128);
573            }
574        }
575        return utftext;
576    },
577
578    // private method for UTF-8 decoding
579    _utf8_decode : function (utftext) {
580        var string = "";
581        var i = 0;
582        var c = c1 = c2 = 0;
583
584        while ( i < utftext.length ) {
585
586            c = utftext.charCodeAt(i);
587
588            if (c < 128) {
589                string += String.fromCharCode(c);
590                i++;
591            }
592            else if((c > 191) && (c < 224)) {
593                c2 = utftext.charCodeAt(i+1);
594                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
595                i += 2;
596            }
597            else {
598                c2 = utftext.charCodeAt(i+1);
599                c3 = utftext.charCodeAt(i+2);
600                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
601                i += 3;
602            }
603        }
604        return string;
605    }
606};

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.