(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();