(Dateiname bei FINDOLOGIC: structured_autocomplete.js)
Bei der JavaScript-Datei findologic_suggest.js handelt es sich um das Suggest-Script von FINDOLOGIC, das in Abhängigkeit zu den beiden jQuery-Komponenten jquery.preload.js und jquery.autocomplete.js steht. Es muss immer sichergestellt sein, dass deren Code vor findologic_suggest.js geladen wurde.
Wegweiser: Integration der FINDOLOGIC Suggest-Funktion
// ====================================== var FlAutocomplete = FlAutocomplete || {}; // --------------------------------------------------------- FlAutocomplete.AbstractAutocomplete = function(options) { // Define class names this.formIdPrefix = "flAcArticle_"; this.formClass = 'article_order_form'; this.shoppingCartWrapper = 'shopping_cart_wrapper'; this.basketButtonClass = 'button_add_basket_ajax'; this.increaseAmountButtonClass = 'button_increase_amount'; this.decreaseAmountButtonClass = 'button_decrease_amount'; this.amountWrapper = "amount_wrapper"; this.inputAmountFieldClass = 'amount_field'; this.cartButtonWrapper = 'cart_button_wrapper'; /* Maximum articles which can be added into cart */ this.itemLimit = 999; }; FlAutocomplete.AbstractAutocomplete.prototype = { /** * Returns the html for the cart button column as jquery object * @param id The article id * @return jQuery-Object */ getCartHtml : function(id) { return; }, /** * Adds item to cart * @param id The article id */ addToCart : function(id) { return; }, /** * Adds onClick events to shopping cart button and in/decrease buttons * @param id The article id */ registerEvents : function(id) { return; }, /** * Changes the value of the article-number field in a row, depending on operation * @param integer id The article id * @param integer operation Wheter '1' increase or '-1' decrease */ changeNumberOfArticles : function(id, operation, callback) { var currentForm = this.getFormWrapperById(id), amountField = currentForm.find('.' + this.inputAmountFieldClass), currentValue = parseInt(amountField.val(), 10), newValue = 0; if (!currentValue || isNaN(currentValue)) { currentValue = 0; } newValue = currentValue + operation; // Either +1 or -1 if (currentValue >= this.itemLimit && operation > 0) { newValue = currentValue } // Can not be negative newValue = newValue < 1 ? 1 : newValue; // Set new value amountField.val(newValue); if (typeof callback === 'function') { callback(newValue); } }, /** * Gets the form element by id * @param id The article id * @return jQuery-Object */ getFormWrapperById : function(id) { return jQuery('#' + this.formIdPrefix + id); } }; // --------------------------------------------------------- FlAutocomplete.Plentymarkets = function(options) { FlAutocomplete.AbstractAutocomplete.apply(this, arguments); jQuery.extend(this, options); }; FlAutocomplete.Plentymarkets.prototype = new FlAutocomplete.AbstractAutocomplete(); FlAutocomplete.Plentymarkets.prototype.getCartHtml = function(id, priceId) { // Wrap it twice, because we use .html(), which only returns the inner html var cart = '<div>'; if (id) { cart += '<form id="' + this.formIdPrefix + id + '" data-id="' + id + '" class="' + this.formClass + '">'; cart += '<input type="hidden" name="ActionCall" value="WebActionBasketAddArticle">'; cart += '<input type="hidden" name="ArticleID" value="' + id + '">'; cart += '<input type="hidden" name="ArticlePrice" value="">'; cart += '<input type="hidden" name="ArticleQuantity__AutoField" value="1">'; cart += '<input type="hidden" name="SYS_P_ID" value="' + priceId + '">'; cart += '<div class="' + this.shoppingCartWrapper + '">'; cart += '<div class="' + this.amountWrapper + '">'; cart += '<input type="text" name="ArticleQuantity" class="amount_field" value="1" size="3" maxlength="3">'; cart += '<button type="button" value="1" class="' + this.increaseAmountButtonClass + '">+</button>'; cart += '<button type="button" value="-1" class="' + this.decreaseAmountButtonClass + '">-</button>'; cart += '</div>'; cart += '<div class="' + this.cartButtonWrapper + '">'; cart += '<span class="button"><button type="button" class="' + this.basketButtonClass + '" data-id="' + id + '">In den Warenkorb</button></span>'; cart += '</div>'; cart += '</div>'; cart += '</form>'; } cart += '</div>'; return jQuery(cart); }; // Unbind all binded events FlAutocomplete.Plentymarkets.prototype.unbindEvents = function(list) { list.off('click', '.' + this.basketButtonClass); list.off('click', '.' + this.amountWrapper); list.off('click', '.' + this.increaseAmountButtonClass + ', .' + this.decreaseAmountButtonClass); }; FlAutocomplete.Plentymarkets.prototype.bindEvents = function(list) { // Because every autocomplete requests calls this method, it binds a new event on the elements // remove first all binded events this.unbindEvents(list); var self = this; // AddToCart buttons list.on('click', '.' + this.basketButtonClass, function(e) { var id = jQuery(this).data('id'); self.addToCart(id); e.stopPropagation(); e.preventDefault(); }); // Do not trigger click (on the parent row element), when clicking in amount div list.on('click', '.' + this.amountWrapper, function(e) { e.stopPropagation(); e.preventDefault(); }); // Number of article buttons list.on('click', '.' + this.increaseAmountButtonClass + ', .' + this.decreaseAmountButtonClass, function(e) { var button = jQuery(this), correspondingForm = jQuery(this).parents('form'), id = correspondingForm.data('id'), operation = parseInt(button.val(), 10); self.changeNumberOfArticles(id, operation, function(newValue) { correspondingForm.find('input[name="ArticleQuantity__AutoField"]').val(newValue); }); e.stopPropagation(); e.preventDefault(); }); }; // This is just a hack method, because in our test shop it does not update the basket amount // There is probably a plenty function which does it FlAutocomplete.Plentymarkets.prototype.updateBasketQuantity = function(id) { var currentForm = this.getFormWrapperById(id), amountField = currentForm.find('.' + this.inputAmountFieldClass), basket = jQuery('#basket_quantity'), amountFieldValue = parseInt(amountField.val(), 10), currentBasketValue = parseInt(basket.html(), 10), value = currentBasketValue + amountFieldValue; basket.html(value); }; FlAutocomplete.Plentymarkets.prototype.addToCart = function(id) { if (id) { var formData = this.getFormWrapperById(id).serialize(); // Check if plenty function is defined if (typeof plentyAjaxRequest2 === 'function') { plentyAjaxRequest2('/WebAjaxBase.php?Object=article@BasketAddArticle&' + formData); // Change shopping cart amount text (until we know how plenty does it) this.updateBasketQuantity(id); } else { throw "plentyAjaxRequest2() does not exist"; } } }; // $Revision: 250 $ // Maximale Bildbreite für die Thumbnail-Bilder in der Liste maxWidthThumb = 40; // Maximale Bildhöhe für die Thumbnail-Bilder in der Liste maxHeightThumb = 40; // Maximale Bildbreite für die Vorschau-Bilder bei Mouseover maxWidthPreview = 200; // Maximale Bildhöhe für die Vorschau-Bilder bei Mouseover maxHeightPreview = 200; // Benennung der Kategorien in der Liste itemLabel = { 'title':'Produktname', 'cat':'Kategorie', 'vendor':'Hersteller', 'ordernumber':'Artikelnummer' }; function initAutocomplete(autocompleteField) { if (typeof autocompleteField == 'undefined' || autocompleteField.length == 0) { alert("Autocomplete: Search field not found"); return; } function format(item, autocompleteObject) { result = ''; item.outputCat = itemLabel[item.cat]; // Ausgabecode für die einzelnen Treffer if (item.image) { result += '<td class="ac_image" align="center" width="' + maxWidthThumb + 'px" height="' + maxHeightThumb + '"><img src="' + item.image + '" alt="Thumbnail" style="display:none;" /></td>'; } else { result += '<td><span style="display:block;width:' + maxWidthThumb + 'px;height:' + maxHeightThumb + 'px;"> </span></td>'; } result += '<td class="ac_name" valign="top">' + item.name + '</td>'; // Only add basket columns, if cat is title if (item.cat === 'title') { // Price var price = item.price ? item.price : ""; result += '<td class="ac_price" valign="top">' + price + '</td>'; // BasePrice var basePrice = item.basePrice ? item.basePrice : ""; result += '<td class="ac_baseprice" valign="top">' + basePrice + '</td>'; // Cart Button var productId = item.parameters.identifier; if (productId && autocompleteObject) { var cartHtml = autocompleteObject.getCartHtml(productId, item.priceId); result += '<td class="ac_cartOption" valign="top">' + cartHtml.html() + '</td>'; } } else { result += '<td></td>'; result += '<td></td>'; result += '<td></td>'; } return result; } autocompleteField.autocomplete('/$FL/structured_autocomplete.php', { max: 10, minChars: 2, dataType: "json", cacheLength: 1, matchSubset: false, autoFill: false, selectFirst: false, scrollHeight: 500, scroll: false, width: false, parse: function(data) { suggestions = jQuery.map(data.suggestions, function(row) { row = row.split("|"); lastCat = ""; /* sometimes the suggestion text contains the | symbol, so re-join it manually, leaving out the last two parts */ text = row.slice(0, row.length - 2).join("|"); return { data: {name: text, cat: row[row.length - 2], count: row[row.length - 1]}, value: text, result: text } }) // for (i in suggestions) { // suggestions[i].data.image = data.images[i]; // suggestions[i].data.parameters = data.parameters[i]; // /* remove the .result if there are parameters present, so the submitted query is empty */ // if (!jQuery.isEmptyObject(data.parameters[i])) { // suggestions[i].result = ""; // } // } jQuery(suggestions).each(function(i) { suggestions[i].data.image = data.images[i]; suggestions[i].data.parameters = data.parameters[i]; if (data.prices) { suggestions[i].data.price = data.prices[i]; } else { suggestions[i].data.price = null; } if (data.basePrices) { suggestions[i].data.basePrice = data.basePrices[i]; } else { suggestions[i].data.basePrice = null; } if (data.priceIds) { suggestions[i].data.priceId = data.priceIds[i]; } else { suggestions[i].data.priceId = null; } if (data.shoppingCart) { suggestions[i].shoppingCart = data.shoppingCart; } else { suggestions[i].shoppingCart = false; } if (data.shopsystem) { suggestions[i].shopSystem = data.shopsystem; } else { suggestions[i].shopSystem = null; } if (!jQuery.isEmptyObject(data.parameters[i])) { suggestions[i].result = ""; } }); return suggestions; }, formatItem: function(item, autocompleteObject) { return format(item, autocompleteObject); }, extraParams: getExtraParams(autocompleteField[0]), highlight: function(row, term) { /* remove trailing and leading whitespace, not relevant for highlighting */ term = term.replace(/^\s+/, ""); term = term.replace(/\s+$/, ""); var titleColumn = row.find('.ac_name'), value = titleColumn.html(); /* * replace non-alphanumeric characters with the "|", effectively splitting the term so all words will be highlighted * \W does not match Umlaute, so manually build a negated character class that includes them explicitly */ /* Cyrillic characters are not part of the \w character class so include them explicitly */ var cyrillicCharacters = "\u0400-\u04FF"; var highlightRegex = term.replace("/[^\wäöüÄÖÜß" + cyrillicCharacters + "]+/g", "|"); var highlightedTerm = value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + highlightRegex + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<span class='flHighlight'>$1</span>"); titleColumn.html(highlightedTerm); return highlightedTerm; }, }) } jQuery(function() { initAutocomplete(autocompleteField); }); // preloads images function preloadImages (dom, maxW, maxH) { jQuery(dom).preload({ onComplete:completePL, treshhold:5 }); function completePL (data) { if(data.found) { resizeImage(jQuery(data.original), maxW, maxH); } } } // resizes image to given dimensions function resizeImage (original, maxW, maxH) { if (original.width() > maxW || original.height() > maxH) { if (original.width() * maxH > original.height() * maxW) { newW = new String(maxW) + "px"; newH = new String(maxW * original.height() / original.width()) + "px"; } else { newH = new String(maxH) + "px"; newW = new String(maxH * original.width() / original.height()) + "px"; } } else { newW = new String(original.width()) + "px"; newH = new String(original.height()) + "px"; } original.css({width:newW,height:newH}); original.show(); } // starts preview on mouseover function showPreview() { xOffset = -5; yOffset = 5; jQuery('.ac_results table tr').live('hover', function(e){ src = jQuery(this).children('td').children('img').attr('src'); if (src && jQuery(this).children('td').children('img').css('display') != 'none') { if (e.type == 'mouseenter' || e.type == 'mouseover') { jQuery('body').append('<div id="preview"><img src="' + src + '" alt="Preview" style=display: none;" /></div>'); preloadImages('#preview img', maxWidthPreview, maxHeightPreview); jQuery("#preview").css("top",(e.pageY - xOffset) + "px").css("left",(e.pageX + yOffset) + "px").fadeIn("fast"); } else { jQuery('#preview').remove(); } } } ); jQuery('.ac_results table tr').live('mousemove', function(e){ var bodyW = jQuery('body').outerWidth(); var bodyH = jQuery('body').outerHeight(); var imgW = (e.pageX + yOffset + jQuery('#preview').width()); var imgH = (e.pageY - xOffset + jQuery('#preview').height()); var imgL = (e.pageX + yOffset); var imgT = (e.pageY - xOffset); if (imgH > bodyH) { imgT = (e.pageY-jQuery('#preview').height()+xOffset); } if (imgW > bodyW) { imgL = (e.pageX-jQuery('#preview').width()-yOffset); } jQuery("#preview").css("top",imgT + "px").css("left",imgL + "px"); } ); jQuery(document).keydown(function(e) { if (e.keyCode == 27) { jQuery("#preview").remove(); } }); } /* append filter parameters to the form before it is submitted */ function addParameters(form, data, name, depth) { /* * Attributes that were added by the autocomplete need to be removed * before new ones are added. Use a jQuery data attribute to store which * attributes were added by the autocomplete */ var addedByAutocompleteKey = "added-by-autocomplete"; /* Remove attributes added by the autocomplete */ jQuery(form).find(":input").each(function() { var isAddedByAutocomplete = jQuery(this).data(addedByAutocompleteKey); if (isAddedByAutocomplete) { jQuery(this).remove(); } }); for (i in data) { var encodedName = encodeURI(i); subname = (depth > 0 ? name + "[" + encodedName + "]" : encodedName); if (typeof data[i] != "object") { var attributeElement = jQuery("<input/>").attr("type", "hidden").attr("name", subname).val(data[i]); /* mark it as being added by the autocomplete */ attributeElement.data(addedByAutocompleteKey, true); jQuery(form).append(attributeElement); } else { addParameters(form, data[i], subname, depth + 1); } } } autocompleteField.result(function(event, data, formatted) { addParameters(event.target.form, data.parameters, "", 0); }); function getExtraParams(input) { parameters = getFormValues(input); parameters.type = "structured"; return parameters; } /* collect all form values to be submitted with the autocomplete request */ function getFormValues(input) { inputs = {}; jQuery(jQuery(input.form).serializeArray()).each(function(i, j) { inputs[j.name] = j.value; }); inputs.query = function() { return jQuery(input).val(); }; return inputs; } showPreview();